【筆記】從 Google 地圖爬取中文店家評論,透過 TensorFlow Lite 和 Firebase 進行文本分類,並將模型添加到 Android 應用上
這幾天碰巧看到這個東西,覺得有點有趣,初步先簡單學習了一下,本文簡單記錄一下學習過程
文本分類是根據文本內容為其分配標籤或類別的過程。它是自然語言處理 (NLP) 的基本任務之一,具有廣泛的應用,例如情感分析、主題標記、垃圾郵件檢測和意圖檢測。
情感分析是使用文本分析技術對文本數據中的情感(積極、消極和中性)進行解釋和分類。情緒分析使企業能夠在在線對話和反饋中識別客戶對產品、品牌或服務的情緒。
以下我們學習構建一個用於情感分析的機器學習模型,特別是將文本分類為正面或負面。這是二元分類(或二類分類)的一個示例,是一種重要且廣泛適用的機器學習問題。
我們使用 TF Lite Model Maker 訓練 TensorFlow Lite(TF Lite) 文本分類模型,來預測給定文本的情緒,然後將這個 TF Lite 模型部署到 Firebase 機器學習(Firebase ML),並且從 Android 應用中訪問它。最後使用 TF Lite 任務庫將 TF Lite 情感分析模型集成到您的應用程序。
首先在 Google Colab 中打開 Train text classification models with TFLite Model Maker 這個筆記本。
註:“Colab” 是 Colaboratory 的簡稱,是 Google Research 團隊開發的一款產品。在 Colab 中,任何人都可以通過瀏覽器編寫和執行任意 Python 代碼。它尤其適合機器學習、數據分析和教育目的。從技術上來說,Colab 是一種託管式 Jupyter 筆記本服務。用戶無需設置,就可以直接使用,同時還能獲得 GPU 等計算資源的免費使用權限。
這個範例筆記本,使用 SST-2 (Stanford Sentiment Treebank),它是 GLUE 基準測試中的一項任務。其中包含 67,349 條用於訓練的電影評論和 872 條用於測試的電影評論。數據集有兩個類:正面電影評論和負面電影評論。我們將用它來訓練一個文本分類模型。
由於這個教學範例是2020年發佈的,目前(2023年8月),Colab 的 Python 版本已經更新到 3.10,所以如果直接執行這個筆記本,預期會在執行 Install the required packages(安裝 TF Lite Model Maker)這一步時發生錯誤。
首先,如果使用的是免費版 Colab,它會在安裝 Python 套件時,就吃光免費方案的 78GB 硬碟空間,然後出現錯誤信息 ERROR: Could not install packages due to an OSError: [Errno 28] No space left on device。(Stack Overflow上面的討論)
升級到付費的 Colab Plus 之後,再執行一次筆記本看看,最後仍然會在執行 from tflite_model_maker import configs 這行代碼時,出現錯誤信息 ModuleNotFoundError: No module named 'tflite_model_maker'。
我查了一下,好像是 tflite-model-maker 在 Python 3.10 版本上的已知問題(參考:GitHub 上面的 Issue 討論)
為了執行這個筆記本來訓練 TL Lite 模型,我們需要修改一下筆記本,我們使用 Python 3.9 版本作為環境,新的筆記本在這裡 -> Tflite_Model_Maker_Python_3_9.ipynb
首先將舊範例筆記本裡面的 Python 代碼,另存成 model.py 腳本,內容如下:
然後將這個腳本上傳到 Google Drive 雲端硬碟上,稍後會從 Colab 筆記本上直接 Mount 雲端硬碟,來存取這個 Python 腳本。
在新的筆記本裡面,透過 drive.mount('/content/gdrive') 指令,就能將 Google Drive 雲端硬碟掛載到 Colab 硬碟的目錄下(註:執行時會跳出對話框,必須先同意授權),這樣我們就可以在筆記本上存取雲端硬碟中的 model.py 腳本了,然後下面這行命令,是將雲端硬碟中的 model.py 腳本,複製一份到 Colab 本地目錄下。
!cp /content/gdrive/MyDrive/tflite-model-maker-workaround/model.py .
最後以 python /content/model.py 指令,來執行腳本
然後大約幾分鐘後,就會看到產生了兩個 model.tflite 模型到 average_word_vec 和 mobilebert 兩個目錄下,點擊檔案右側的三個小圓點 icon 即可將模型下載到本地端。
現在有了 TF Lite 模型,接下來我們要將它上傳到 Firebase ML 上,首先從 GitHub 上面下載 Sample Code,我們直接使用 finish 資料夾裡面的 project,它的 Package 名稱是 org.tensorflow.lite.codelabs.textclassification,接著進入 Firebase 控制台,建立一個 Android 應用專案,專案的 PackageName 需填寫上面的名稱。
建立完成後,點選專案左側導航面板中的 機器學習(Machine Learning)頁面,然後選擇 Customize,在對話框輸入模型名稱並點選上傳,就能將稍少訓練出來的 TF Lite 模型上傳到 Firebase 服務上面。
然後在 Android Studio 開發環境中,開啟稍早下載的專案,編譯並安裝到手機上執行,現在我們已經有了一個簡單的文本分類 AI 應用。
這個範例使用的是英文的 Movie Reviews 資料集,我們也可以嘗試使用不同類型的資料集,來訓練 TF Lite 模型,看會得到怎樣的結果。
作為例子,我們從 Kaggle 上面找到一個年份比較早的 Yelp Restaurant Reviews 英文資料集,現在我們可以下載這個餐廳評論資料集,然後將我們不需要的欄位刪除(只保留rating分數和評論內容兩個欄位),然後將資料集切分為訓練集和驗證集,分別儲存為 train.tsv 和 dev.tsv 文件。
最後將這兩個 .tsv 文件上傳到 Google Drive 雲端硬碟上(這裡我將它放到一個名為 SST-1 的資料夾裡)。
註:TSV 和 CSV 之間的唯一區别是,TSV 使用制表符 \t 字符作为分隔符,而不是 CSV 格式中的逗號。
接著編輯 model.py 腳本,用以根據這個餐廳評論資料集,產生 TF Lite 模型。腳本內容如下(註:這個腳本和先前的內容並沒有太大差別,只是些修改了資料集來源,我們將裡面透過網路下載資料集的代碼刪除了):
然後將修改後的 model.py 腳本上傳到 Google Drive 雲端硬碟,最後編輯 Tflite_Model_Maker_Python_3_9.ipynb 筆記本,在執行python腳本前,加入以下三行筆記:
編輯後的筆記本內容大致如下:
最後點選筆記本上面的執行階段 -> 全部執行,幾分鐘後就可以得到,這20,000筆餐廳評論資料集所訓練出來的 TF Lite 模型了。
將此模型命名為 yelp_mobilebert 並上傳到 Firebase ML,然後修改 Android 專案中 MainActivity.java 文件裡的模型名稱,這樣就可以在 Android 應用程式中,使用這個餐廳評論的文本分類模型了。
downloadModel("yelp_mobilebert");
在筆記本裡面,可以選擇使用 average_word_vec(平均單詞嵌入向量)或是 BERT 來作為 Model Maker 的模型架構。TensorFlow Lite Model Maker 目前支持 MobileBERT,平均單詞嵌入向量和 BERT-Base 模型。
若是使用平均單詞嵌入向量作為模型架構,我們可以透過 NLClassifier API 將模型集成到 Android 應用程式中。但如果要以 MobileBERT 架構對文本分類模型進行行訓練,那就需要使用 TensorFlow Lite Task Library 所提供的 BertNLClassifier API 將訓練後的模型集成到 Android 應用程式中。
也就是將 MainActivity.java 文件中,預設的 textClassifier 類型宣告:
private NLClassifier textClassifier;
修改為
private BertNLClassifier textClassifier;
另外在 downloadModel() 裡面的代碼也要修改,請將:
textClassifier = NLClassifier.createFromFile(model.getFile());
修改為
textClassifier = BertNLClassifier.createFromFile(model.getFile());
然後就可以編譯執行應用程式了!
我試著輸入中文和英文來做測試,因為模型是用英文資料集訓練的,所以可以發現,當使用中文輸入文本時,模型的判斷結果不是很理想,但若改用英文輸入文本,結果就會比較理想(結果如下截圖)。
為了獲取中文的餐廳評論資料集,我們可以從 Google 地圖上取得中文餐廳評論,來訓練文本分類模型。
怎麼獲得評論資料呢?當然不可能是人工一筆一筆去輸入,我找到一個叫做 SerpAPI 的網路服務(也有免費方案,只是每月的扣打很少),來抓取Google搜尋的內容。也就是說,Google 搜尋查到的資料,都可以從 SerpAPI 的服務取得。
除了 Google 搜尋的內容,SerpAPI 還支援其它常見的搜尋應用,比方抓取 YouTube 影片跟 Google 地圖上的餐廳、用戶評論等資料。SerpAPI 的 使用方式,它的網站上已有清楚的說明。
在這個簡單的應用中,我只會用到它的 Google Maps Local Results API,以及 Google Maps Reviews API 這兩個 API,前者用來獲取某地點(給定經緯度)附近的店家列表,並從資料中取得店家的 data_id,後者可以傳入 data_id 作為參數,獲取該店家的所有用戶評論。
然後我寫了一個簡單的 Python 腳本,它可以根據給定的經緯度,以及query的地點類型(例如:燒肉店),來爬取 Google 地圖上,該座標位置附近的燒肉店。
SerpAPI 對 API 查詢的結果做了分頁機制,每一頁只會顯示最多20筆店家資料,所以我在腳本中加入查詢下一頁的功能,會查詢到最後一頁為止。然後再透過每個店家的 data_id 去獲取該店家的用戶評論。
SerpAPI 的評論查詢結果也有做分頁處理,每一頁只會顯示10筆用戶評論資料,以我也在腳本中加入查詢下一頁的功能,但是我設定最多只查詢到第20頁。
原因是 SerpAPI 的評論查詢結果,預設是以 the most relevant reviews 來排序查詢結果,太後面的頁面,經常出現只有 rating,而沒有評論文本的結果,所以這裡限制每個店家最多只查詢20頁的評論(也就是最多200筆評論)。
此外這個腳本,也過濾掉沒有文本內容、字數少於10個字,以及沒有包含中文內容的評論。原因是我在測試過程中,發現很多店家的評論,經常會出現:好吃、好好吃、真的好吃、Good,這一類本文很簡短的5顆星評論,一看就是店家洗出來的,我們將這一類的廢文給過濾掉。
最後將爬取到的 rating 值(1顆星~5顆星)和評論文本,儲存到一個包含 label 和 sentence 欄位的 google_reviews.csv 文件中。
我們過濾掉下面這種無用的評論內容
只保留下面這種比較有用的評論內容
以下是這個 Python 腳本的完整內容:
其中,search_restaurants() 裡面的 ll參數中,經緯度後面帶的15z,代表的是地圖的縮放等級(zoom level),SerpAPI 可接受的數值從 3z~21z,後者代表完全 zoomed in。這裡我設定為15z。不同的 zoom level 值會影響店家的搜尋結果。
經實際測試,若使用 SerpAPI 的 Developer Plan 查詢額度(每月5000次查詢,每小時不可超過1000次查詢),差不多能在可用的額度之內,爬取並儲存到 17,000 多筆經過篩選後的中文用戶評論。
註:如果在 serp.py 腳本中,修改評論內容字數長度的篩選條件(例如10 -> 30個字),那麼最後 CSV 文件中,經過篩選後的評論數量,理論上會少於 17,000 筆這個數目。
註:有些類型的店家,不太可能會出現在郊區或者山上,例如你設定了一個宜蘭大同鄉山區的經緯度座標,然後query設定燒肉店,那麼可能根本查詢不到店家,只會浪費掉 API 查詢額度。所以在設定經緯度時,最好是選擇市區地點,才能盡可能爬取到最大數量的店家評論。
當爬取足夠多的用戶評論後,就可以用這個中文餐廳評論資料集,去訓練中文的文本分類模型,然後將模型添加到 Android 應用程式上。
在有限的時間及查詢額度下,我產生了一個包含約 22,450 筆中文店家評論的 CSV 文件,不過發現在 Mac 電腦上,使用 Numbers 和 Excel 軟體將這個中文的 CSV 文件轉換成 TSV 文件格式後,在 Colab 筆記本上執行 python /content/model.py 指令時,會出現 UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 2: invalid start byte 這樣的錯誤信息,看起來是文件編碼相關的問題。
最後想到一個簡單快速的解決方案,就是在 Google 試算表上面載入這個 CSV 文件,然後點選檔案->下載->Tab鍵分隔值檔案 (.tsv),最後將這個 TSV 文件丟到雲端硬碟並掛載給 Colab 腳本使用,上面的問題就不見了。
註:這裡我只是學習和測試目的,所以只用兩萬多筆資料來訓練模型,在正式的場合,這樣的資料量是遠遠不夠用的。
最後執行一下 Android 範例應用程式,來看看這個文本分類模型的結果。