2019年6月21日 星期五

自製基本的文章分類程式 - 使用Python

  在版主的現職工作中,文章分類的需求越來越高,且索取分類資訊的頻率越來越頻繁,因此必須從以往仰賴特定領域專業人士進行分類,轉變為使用機器學習技術替人們做預先分類的工作,如此一來可以降低人工分類的負擔、改善人工分類準則的不穩定性,且大幅縮減分類所需的時間與金錢成本。

  本篇文章接續〈自製直覺的文章分類程式- 使用Python〉這篇文章的精神,將原本使用集合(Set)進行分類的邏輯大幅強化,改為使用基本的機器學習演算法進行實作,並降低對迴圈的依賴,以提升程式的效率、可讀性,與可維護性,並期許版主自己能夠繼續提升使用這些程式工具的純熟程度。

Python套件設定

jieba-tw結巴中文斷詞台灣繁體版本
l   由於本文章分類程式分析的資料為台灣繁體中文,因此選擇使用交通大學的特化版本:https://github.com/APCLab/jieba-tw

MatplotlibSeaborn套件以微軟正黑體顯示中文
l   以下以Windows系統修改設定檔的方式做為解決方法,詳情可參考〈解決Python 3 MatplotlibSeaborn視覺化套件中文顯示問題〉,是一篇說明完整的教學,而此次要執行的文章分類程式,需同時使用MatplotlibSeaborn套件,故兩者皆須修改。
l   步驟一-在matplotlibrc 預設路徑找到matplotlibrc設定檔並修改之:
1.
Anaconda版本的Python為例,matplotlibrc所在的預設路徑通常是C:\Users\使用者名稱\AppData\Local\Continuum\anaconda3\Lib\site-packages\matplotlib\mpl-data
2.
刪除font.familyfont.serif最前方的註解(#),並在font.serif加入Microsoft JhengHei
l   步驟二-將微軟正黑體ttf檔命名為msj.ttf,放置於字型檔指定路徑:
Anaconda版本的Python為例,Matplotlib字型檔指定路徑通常是C:\Users\使用者名稱\AppData\Local\Continuum\anaconda3\Lib\site-packages\matplotlib\mpl-data\fonts\ttf
l   步驟三-刪除.matplotlib快取資料夾:
.matplotlib
快取資料夾預設路徑通常是C:\Users\使用者名稱
l   步驟四-執行Script時使用rcParams參數指定字體:
import matplotlib.pyplot as plt
plt.rcParams["font.sans-serif"] = ["Microsoft JhengHei"]
plt.rcParams["axes.unicode_minus"] = False

import seaborn as sns
sns.set(font = ["sans-serif"])
sns.set_style("whitegrid", {"font.sans-serif":["Microsoft JhengHei"]})

資料樣貌

  此次要進行分類的資料取自政府研究資訊系統GRB,一般民眾無需登入該系統即可查閱相關計畫資料。「分類比例」欄位表示該計畫分類至「分類資料夾」之類別的比例,其中「分類資料夾」包含了41個類別,共16994筆計畫分類皆為人工完成;下列表格以其中一筆資料做為範例,我們考慮使用計畫中文名稱、計畫英文名稱、中文關鍵詞、英文關鍵詞、計畫中文摘要、計畫英文摘要共6個欄位進行分類(Classification)與集群(Clustering)

表一、進行分類與集群的資料樣貌
分類比例
分類資料夾
計畫中文名稱
計畫英文名稱
1
電動車輛
應用六標準差於綠能電動機車水冷式馬達製程品質提升之研究
The Process Quality Improvement of the Green Electric Scooter Water-Cooling Motor by Using the Six-Sigma Method
中文關鍵詞
英文關鍵詞
GOGORO綠能電動機車;六標準差;工業4.0;反應曲面法
GOGORO green electric locomotive motorsSix-SigmaIndustry 4.0Response Surface Methodology
計畫中文摘要
計畫英文摘要
為避免全球暖化問題日益嚴重,根據聯合國《聯合國氣候變化框架公約》與《京都議定書》相關會議決議,全世界各主要國家皆將節能減碳當作國家最重大的施政方針,綠能產業已經被確定為2010-2020年間,全世界各主要國產業發展的...省略...計法與反應曲面法得到最佳參數設定,並套入工業4.0概念協助廠內刀具室管理,最後建立SOP與管制圖來維持產品品質穩定。本計劃將此整合評估模式實際導入工廠管理系統,期能幫助業者有效提升製程品質,以增加產業競爭力。
To avoid getting global warming severer, according to the relevant resolutions of the United Nations Framework Convention on Climate Change and Kyoto Protocol, major countries worldwide all regard carbon reduction as the most significant national policy, and the green industry has been confirmed as the highlight of industrial development for... omitted... to assist in the tool room management of the factory. Last, SOP will be established to stabilize the product’s quality. It is planned to practically introduce the integrated evaluation model into the factory management system, expecting to help the manufacturer enhance their process quality as well as their industrial competitiveness.

集群(Clustering

  首先碰到的工作需求,是將沒有分類的資料,使用集群方法進行預分類;以下使用範例資料41個類別中的「電動車輛」做為基礎,共1155筆計畫嘗試分為7個群組,若使用中文欄位進行集群,則須加上中文斷詞步驟,再使用半形空格組裝,以利輸入TfidfVectorizer()進行特徵工程。

Python程式碼
l   中文斷詞處理後儲存在Excel檔最後一欄,故僅需執行一遍:
def chineseTackle():
    import openpyxl
    import jieba
   
    aBook = openpyxl.load_workbook("
電動車輛資料庫.xlsx")
    aSheet = aBook.active
    aSheet["V1"] = "
計畫中文摘要(修改)"
   
    for rowNum in range(2, aSheet.max_row + 1):
        #
跳過計畫中文摘要是空值的計畫
        if aSheet.cell(row = rowNum, column = 8).value == None:
            continue
        #
中文斷詞
        content = aSheet.cell(row = rowNum, column = 8).value
        words = jieba.lcut(content, cut_all = False)
        #
半形空格組裝
        aSheet["V" + str(rowNum)] = " ".join(words)
       
    aBook.save("
電動車輛資料庫.xlsx")

chineseTackle()
l   集群以k-means Clustering為例:
#
資料
import pandas as pd
data = pd.read_excel("
電動車輛資料庫.xlsx")
#
選用計畫中文摘要(修改)不是空值的計畫
data = data[data["
計畫中文摘要(修改)"].notnull()]
data = data.reset_index()

#
特徵工程、套用Estimator
from sklearn.pipeline import make_pipeline
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.cluster import KMeans
model = make_pipeline(TfidfVectorizer(), KMeans(n_clusters = 7))
data_fit = model.fit(data["
計畫中文摘要(修改)"]).predict(data["計畫中文摘要(修改)"])

#
輸出
data["Cluster_KMeans"] = pd.Series(data_fit)
data.pop("index")
data.to_excel("
電動車輛資料庫Cluster_KMeans.xlsx", "電動車輛Cluster_KMeans")

集群結果

  集群的類別數目、使用的欄位,以及使用的演算法都是可以調整的方向,即使使用同一種組合,也會因為演算法的隨機程序而導致不同的結果,重要的是,我們必須以人工的方式檢視集群的內容,才能判斷分群的良窳,並替這些群組命名。

  此外,各群組的計畫數目也會是我們關心的要點,如果所有計畫都集中在少數一、二個群組,而其它群組僅分有稀少計畫(如表二計畫英文摘要與Spectral Clustering的組合),這可能就值得探討,是不是選擇的集群類別數目不佳?還是演算法本身並不適合這份資料?同樣地,必須以人工的方式檢視集群的內容才行。

表二、各群組的計畫數目

計畫中文摘要
計畫英文摘要
k-means Clustering
Cluster 0: 61    筆計畫
Cluster 1: 124  筆計畫
Cluster 2: 272  筆計畫
Cluster 3: 46    筆計畫
Cluster 4: 213  筆計畫
Cluster 5: 58    筆計畫
Cluster 6: 120  筆計畫
Cluster 0: 133  筆計畫
Cluster 1: 61    筆計畫
Cluster 2: 251  筆計畫
Cluster 3: 35    筆計畫
Cluster 4: 62    筆計畫
Cluster 5: 127  筆計畫
Cluster 6: 122  筆計畫
Spectral Clustering
Cluster 0: 100  筆計畫
Cluster 1: 22    筆計畫
Cluster 2: 54    筆計畫
Cluster 3: 98    筆計畫
Cluster 4: 516  筆計畫
Cluster 5: 45    筆計畫
Cluster 6: 59    筆計畫
Cluster 0: 350  筆計畫
Cluster 1: 7      筆計畫
Cluster 2: 91    筆計畫
Cluster 3: 73    筆計畫
Cluster 4: 6      筆計畫
Cluster 5: 17    筆計畫
Cluster 6: 247  筆計畫

分類(Classification)

  接著碰到的工作需求,是將已分類的資料,透過監督式學習的分類方法建立模型,未來若需要對新的計畫資料進行分類,只要將新的計畫資料輸入模型,電腦即自動幫我們做好分類的工作;以下使用完整範例資料共16994筆計畫,隨機以其中80%的計畫做為訓練資料,而剩下20%的計畫則做為測試資料,以評估機器學習的表現,若使用中文欄位進行分類,一樣須加上中文斷詞步驟,再使用半形空格組裝,以利輸入TfidfVectorizer()進行特徵工程。

Python程式碼
l   中文斷詞處理後儲存在Excel檔最後一欄,故僅需執行一遍:
def chineseTackle():
    import openpyxl
    import jieba
   
    aBook = openpyxl.load_workbook("
綠能資料庫.xlsx")
    aSheet = aBook.active
    aSheet["S1"] = "
計畫中文摘要(修改)"
   
    for rowNum in range(2, aSheet.max_row + 1):
        if aSheet.cell(row = rowNum, column = 8).value == None:
            continue
        content = aSheet.cell(row = rowNum, column = 8).value
        words = jieba.lcut(content, cut_all = False)
        aSheet["S" + str(rowNum)] = " ".join(words)
       
    aBook.save("
綠能資料庫.xlsx")

chineseTackle()
l   分類以Multinomial Naive Bayes Classifier為例:
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

#
解決中文字體繪圖問題
plt.rcParams["font.sans-serif"] = ["Microsoft JhengHei"]
plt.rcParams["axes.unicode_minus"] = False

sns.set(font = ["sans-serif"])
sns.set_style("whitegrid", {"font.sans-serif":["Microsoft JhengHei"]})

#
資料
data = pd.read_excel("
綠能資料庫.xlsx")
#
選用分類比例為1的計畫,才適合做機器學習
selectedData = data.loc[data["
分類比例"] == 1]
#
選用計畫中文摘要(修改)不是空值的計畫
selectedData = selectedData[selectedData["
計畫中文摘要(修改)"].notnull()]

trainData = selectedData["
計畫中文摘要(修改)"]
trainTarget = selectedData["
分類資料夾"]

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(trainData, trainTarget, train_size = 0.8)

#
特徵工程、套用Estimator
from sklearn.pipeline import make_pipeline
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
model = make_pipeline(TfidfVectorizer(), MultinomialNB())
y_fit = model.fit(X_train, y_train).predict(X_test)

#
準確率
from sklearn.metrics import accuracy_score
print(accuracy_score(y_test, y_fit))

#
繪圖
from sklearn.metrics import confusion_matrix
mat = confusion_matrix(y_test, y_fit)
sns.heatmap(mat.T, annot = True, fmt = "d", cbar = False, xticklabels = model.classes_, yticklabels = model.classes_)
plt.xlabel("True Label")
plt.ylabel("Predicted Label")
plt.show()

#
快速的工具函式,可以傳回單一字串的預測結果
def predict_category(s, model = model):
    pred = model.predict([s])
    pred_prob = model.predict_proba([s])[0]
    df = pd.DataFrame({"Label": model.classes_, "Probability": pred_prob.round(3)})
    return pred[0], df

corpus = "
請貼上欲分類的文章(中文必須經過斷詞處理)"
pred, df = predict_category(corpus)
print("Predicted: ", pred, "True :
請貼上人工分類的類別")
print(df)

分類結果

  觀察表三的數值,可以得知監督式學習的分類方法與人工分類的相似程度不高,至少於此範例資料中的表現是不佳的,除了訓練與測試資料的比例、使用的欄位,以及使用的演算法可以調整改善,最主要的問題還是類別過多(41個類別),且不同類別之間的差異不明顯;我們以相似程度最高的組合-計畫中文摘要與Random Forest Classifier的組合,當作探討的例子,見圖一。

表三、監督式學習的分類方法與人工分類的相似程度

計畫中文摘要
計畫英文摘要
Multinomial Naive Bayes Classifier
Accuracy Score: 45.269%
Accuracy Score: 35.710%
k-nearest Neighbors Classifier
Accuracy Score: 27.151%
Accuracy Score: 27.188%
Support Vector Classifier
Accuracy Score: 12.742%
Accuracy Score: 13.101%
Random Forest Classifier
Accuracy Score: 60.108%
Accuracy Score: 51.014%

  從圖一的混淆矩陣可以看出最佳分類對角線是存在的,但是我們可以從此圖發現兩個資料面的問題:一、多數類別的計畫數過少,例如「交換式電源供應器」這個類別,測試資料中只有7筆,就連完整範例資料16994筆計畫中,也才出現111筆而已,對於機器學習擷取特徵而言,似乎有所不足;二、類別之間其實具有樹狀層級關係,觀察圖中「LED材料與元件」、「LED照明與應用」、「LED電路與模組」3個類別,可以發現不只機器無法明確區隔三者的差異,就連專業人士恐怕也難以明確將其區分,事實上這3個類別在人工分類的過程中,皆隸屬「LED照明」這個母分類資料夾,而其它類別也有相同情形,因此在執行機器學習時應加以考慮。

  至於演算法方面,隨機森林(Random Forest)之所以比較適合此範例資料,可能與決策樹(Decision Tree)過度擬合學習資料的特性有關,因此當一般演算法面對此範例資料可能有學習不足之情形,隨機森林演算法相較之下就會表現得比較好。

圖一、計畫中文摘要與Random Forest Classifier組合的混淆矩陣

  最後來談談重複分類,由於監督式學習的分類方法是透過訓練資料建立的模型來進行分類,因此基於演算法的不同,可以對新文章產生運算後相應類別的機率,表四為傳入新的計畫資料至隨機森林模型而得到的預測機率,可以知道該計畫資料之所以歸類為「太陽電池材料與製造設備」,是因為有最高的機率(0.5),而未來若要將該計畫重複分類,可使用模型提供的預測機率,做為「分類比例」的來源依據。

表四、Random Forest Classifier模型估算新文章於各類別的機率
Predicted: 太陽電池材料與製造設備 True: 太陽電池

Label
Probability
0
LED材料與元件
0.0
1
LED照明與應用
0.0
2
LED電路與模組
0.0
3
交換式電源供應器
0.0
4
住宅能源管理系統與控制
0.0
5
其他類型燃料電池
0.0
6
冷氣變頻控制模組
0.0
7
厭氧發酵/光合產氫
0.0
8
同步馬達
0.0
9
固態氧化物燃料電池
0.0
10
太陽光電模組
0.0
11
太陽光電產品與應用
0.0
12
太陽光電發電系統
0.0
13
太陽電池
0.4
14
太陽電池材料與製造設備
0.5
15
微型燃料電池
0.0
16
感應馬達
0.0
17
數位電錶
0.0
18
氫能
0.0
19
無線感測器模組
0.0
20
照明節能
0.0
21
燃料電池材料與組件
0.0
22
燃料電池產品與應用
0.0
23
生物燃料電池
0.0
24
生質乙醇
0.0
25
生質廢棄物
0.0
26
生質沼氣
0.0
27
生質燃油
0.0
28
生質能源與環境
0.0
29
直接甲醇燃料電池
0.0
30
節能診斷模組晶片
0.0
31
能源作物
0.0
32
藻類養殖
0.0
33
變頻器
0.0
34
質子交換模燃料電池
0.0
35
-空氣燃料電池
0.1
36
電動車輛
0.0
37
電子式安定器
0.0
38
電源管理晶片
0.0
39
電能監控管理系統
0.0
40
風力發電
0.0

沒有留言:

張貼留言