聲明:
1)本文由我bitpeach原創撰寫,轉載時請注明出處,侵權必究。
2)本小實驗工作環境為Windows系統下的WEKA,實驗內容主要有三部分,第一是分類挖掘(垃圾郵件過濾),第二是聚類分析,第三是關聯挖掘。
3)本文由於過長,且實驗報告內的評估觀點有時不一定正確,希望拋磚引玉。
(一)WEKA在Ubuntu下的配置
下載解壓
-
下載和解壓weka
。下載:
創建目錄:sudo mkdir /usr/weka。
解壓weka到該目錄:unzip weka-3-6-10.zip -d /usr/weka。
配置Ubuntu的環境變量
-
配置環境變量。還是使用vim 在/etc/environment 中配置。(新增WEKAROOT,修改CLASSPATH)
export WEKAROOT=/usr/weka/weka-3-6-10
export CLASSPATH=.:$JAVA_HOME/lib:$JAVA_HOME/jre/lib:$WEKAROOT/weka.jar
最后別忘了 source /etc/environment,加載新的配置文件。
當然了WEKAROOT換成別的名字也可以的,如WEKA_PATH等,切記如果想換名字的話,首先用echo命令查看環境變量參數,指令如下:
echo $WEKAROOT
然后使用unset命令刪除掉,指令為unset WEKAROOT
最后新建一個WEKA_PATH環境,新建方法仍然是往environment文件里寫入並更新。
運行WEKA
-
運行WEKA,運行命令:cd $WEKAROOT(或者cd $WEKA_PATH)
運行命令:java -Xmx1000M -jar weka.jar(或者java -jar weka.jar)
Java虛擬機進程能構從操作系統那里挖到的最大的內存,以字節為單位,如果在運行java程序的時候,沒有添加-Xmx參數,那么就是64兆,這是java虛擬機默認情況下能從操作系統那里挖到的最大的內存。如果添加了-Xmx參數,將以這個參數后面的值為准,例如本指令下最大內存就是1000兆。
快速啟動WEKA
-
更加快捷的運行weka,輸入以下命令:
sudo touch /usr/bin/weka
sudo chmod 775 /usr/bin/weka
sudo vi /usr/bin/weka
輸入以下代碼:
#!/bin/bash
cd $WEKAROOT 或 cd $WEKA_PATH
java -Xmx1000M -jar weka.jar
保存退出vim
以后要運行weka,只要在命令行里直接鍵入命令 weka 就可以了。這里附上vim編輯的一些經驗:
1)vi 的進入
在 shell 模式下,鍵入vi 及需要編輯的文件名,即可進入vi。
例如:
vi example.txt
即可編輯 example.txt 文件。如果該文件存在,則編輯界面中會顯示該文件的內容,並將光標定位在文件的第一行;如果文件不存在,則編輯界面中無任何內容。如果需要在進入vi 編輯界面后,將光標置於文件的第n 行,則在vi命令后面加上+n 參數即可。例如需要從example.txt 文件的第5 行開始顯示,則使用如下命令:
vi +5 example.txt
2)退出 vi
退出 vi 時,需要在末行模式中輸入退出命令q。
如果在文本輸入模式下,首先按ESC 鍵進入命令模式,然后輸入": "進入末行模式。在末行模式下,可使用如下退出命令:
:q 直接退出如果在文本輸入模式下修改了文檔內容則不能退出
:wq 保存后退出
:x 同 wq
:q! 不保存內容強制退出
(二)WEKA在Windows下的配置
安裝WEKA的EXE程序包
-
下載weka在Windows下的可執行包
官方網站提供兩類Windows下的可執行包,一類是OS直接運行的,另一類是在JVM里運行的,我下載的是基於JVM運行的類型的可執行包。
安裝過程沒有什么可說的,Windows下的安裝過程非常簡單易懂。
運行WEKA
-
運行Weka
運行Weka有兩類方式,一類是通過OS的操作方式運行,如雙擊運行或調用exe等等。還有一類是通過Java命令運行。
OS操作下運行Weka感覺上是沒有什么問題,注意是"感覺上"沒有問題!事實上通過OS運行,由於什么錯誤也沒有,可能發覺不到Weka的一些Warning。直到你需要某項Weka讀取數據庫的功能操作時,才發現怎么也運行不通,而卻不知道為什么。
在調用Java命令運行的過程中就發現,幾個關於數據庫讀取的Warning!處理需要一定時間。相關Warning如下:
| >java –Xmx1024M –jar weka.jar Trying to add database driver (JDBC): RmiJdbc.RJDriver - Error, not in CLASSPATH? Trying to add database driver (JDBC): jdbc.idbDriver - Error, not in CLASSPATH? Trying to add database driver (JDBC): org.gjt.mm.mysql.Driver - Error, not in CLASSPATH? Trying to add database driver (JDBC): com.mckoi.JDBCDriver - Error, not in CLASSPATH? Trying to add database driver (JDBC): org.hsqldb.jdbcDriver - Error, not in CLASSPATH? |
WEKA啟動的驅動報錯
-
彌補數據庫連接的問題方法(Ubuntu和Windows皆出現)
查詢網絡論壇,說linux和windows均會出現此類問題,我在Ubunntu和Win8都配置過了,確實都會出現這些Warning。
查詢網絡論壇的一些大神的實驗記錄與心得,兩大OS的解決辦法基本相同,先說說Ubuntu
1)Ubuntu解決方法
第一步、下載相關數據驅動包,大約有5個jar包,注意Weka3-6對應的這5個jar包版本各自不同,一定要找對版本的這5個jar包。例如:對於Weka(version 3.5.5) 對於RmiJdbc,一定選擇版本3.05或2.5。
名字分別為:hsqldb.jar,idb.jar,mkjdbc.jar,mysql-connector-java-5.1.6-bin.jar,RmiJdbc.jar。
我的Weka版本為3-6-11,使用3-6-8版本的此5個jar包,沒有問題。
第二步、解壓后把這5個jar包拷貝到/usr/lib/jvm/jdk1.7.0_25 /jre/lib/ext目錄下,你的路徑可能和我的不一樣,記着是你的jdk安裝路徑就行了。(吐個槽,在這一點上,Ubuntu比Windows好使多了,復制一下這幾個jar包就行了,就不會報錯了。可是Windows你懂的,修改環境變量起來好累。)
第三步、修改weka數據類型和數據庫數據類型的映射。
(對於這步驟操作,Weka官方wiki提供相關文檔操作方法http://weka.wikispaces.com/Properties+file)
在weka.jar里面有一個文件/weka/experiment/DatabaseUtils.props,記錄了數據庫操作的相關參數。還有很多文件DatabaseUtils.props.msaccess,DatabaseUtils.props.mssqlserver等,分別對應了各個數據庫的操作參數,
如果你使用msaccess,可以把DatabaseUtils.props.msaccess的內容覆蓋DatabaseUtils.props。
如果不對DatabaseUtils.props修改,可能在連接數據庫時一切順利,但在將數據裝入准備預處理時,卻出現找不到數據類型(can not read from database,unknown data type)之類錯誤。
沒關系,在DatabaseUtils.props加入類型映射就OK了。文件中一般有下面的內容(這里是我用mysql對應的文件覆蓋了):
| # JDBC driver (comma-separated list) jdbcDriver=com.mysql.jdbc.Driver # database URL jdbcURL=jdbc:mysql://server_name:3306/database_name # specific data types string, getString() = 0; --> nominal boolean, getBoolean() = 1; --> nominal double, getDouble() = 2; --> numeric byte, getByte() = 3; --> numeric short, getByte()= 4; --> numeric int, getInteger() = 5; --> numeric long, getLong() = 6; --> numeric float, getFloat() = 7; --> numeric date, getDate() = 8; --> date text, getString() = 9; --> string time, getTime() = 10; --> date BigDecimal,getBigDecimal()=11; -->nominal #mysql-conversion CHAR=0 TEXT=0 |
#mysql-conversion下提供的類型一般是不夠的,比如int unsigned就找不到,
所以要加入int是如何映射到weka類型的。
在# specific data types下找到Int對應的java類型,這里是
int, getInteger() = 5; --> numeric
所以在#mysql-conversion下新增INT=5
再加上UNSIGNED類型,INT_UNSIGNED=6(因為unsigned比signed多一倍的數,為防止截斷,要取大的類型)
其他類型的映射依次類推。
注意INT和UNSIGNED之間的下划線,缺了的話錯誤解決不了,我就在這里搞了好久。(Note: in case database types have blanks, one needs to replace those blanks with an underscore, e.g., DOUBLE PRECISION must be listed like this:DOUBLE_PRECISION=2. Notes from http://weka.wikispaces.com/weka_experiment_DatabaseUtils.props#toc4)
第四步,最后最重要的是,把DatabaseUtils.props放到home目錄下,重啟Weka后生效。並請注意:不同的數據庫,書寫方法與格式不同,上面是MySql類型,還有SQL Server2000或Oracle等等,格式是不同的,可百度參考相關格式或論壇博客。
2)Windows解決方法
對於找不到jdbc驅動的問題,主要是修改RunWeka.ini,因為屢次修改classpath都不成功。干脆將jar文件的地址加到weka指定的運行時classpath中。RunWeka.ini的位置在Weka總目錄下,與Weka.jar是平行目錄。打開后內容底部有如下內容:
| # The classpath placeholder. Add any environment variables or jars to it that # you need for your Weka environment. # Example with an enviroment variable (e.g., THIRD_PARTY_LIBS): # cp=%CLASSPATH%;%THIRD_PARTY_LIBS% # Example with an extra jar (located at D:\libraries\libsvm.jar): # cp=%CLASSPATH%;D:\\\\libraries\\\\libsvm.jar # Or in order to avoid quadrupled backslashes, you can also use slashes "/": # cp=%CLASSPATH%;D:/libraries/libsvm.jar cp=%CLASSPATH% |
根據提示,將jdbc驅動jar文件地址附加到cp變量。
比如mysql jdbc驅動(mysql-connector.jar)在E:/jars/下,則加入下面這句:
cp=%CLASSPATH%;E:/jars/mysql-connector.jar
同時把cp=%CLASSPATH%注釋掉,
結果像
cp=%CLASSPATH%;E:/jars/mysql-connector.jar
#cp=%CLASSPATH%
也就是說如果有5個jar包,需要在一行里一個一個寫進,而不能用寫一個5個jar包所在的總目錄來省事。(網上有人說把那5個Jar包解壓到weka路徑下,然后修改CLASSPATH也能解決,我試過很多次,都不可行。)
至於數據類型映射,參見上面Linux環境下的配置。
最后將DatabaseUtils.props文件放在跟Weka.jar同一目錄下即可。
3)Windows下若上述方法失敗,仍然報JDBC錯誤。
解決方法有以下幾種:
一、設置系統里環境變量,因為你在RunWeka.ini內設置了classpath,結果仍然不識別,我想應該為系統里的classpath添加路徑。
二、使用java命令行,利用臨時權限添加classpath的方法
上面兩方法,第一種我試了很多遍沒有成功,第二種java命令會有問題,但最終成功。
第二種方法方法細節:
一、使用如下命令格式,但是中途出現一些問題
java -classpath E:/Weka-3-6/jars/RmiJdbc.jar -jar weka.jar
依然報5個錯誤,如下圖。

查詢相關知識,java的此classpath命令會被-jar屏蔽掉,意思就是說如果java命令里有-jar參數,則會自動屏蔽-classpath添加參數
二、我們嘗試修改指令
java -Xbootclasspath/a:/E:/Weka-3-6/jars/Rmijdbc.jar: -jar Weka.jar
運行后發現少了一個錯誤,我們添加的是Rmijdbc驅動包,減少的錯誤恰好是RJDriverError,說明我們的目前指令成功,如果需要添加多個jar包,則在路徑后面使用分號,逐步添加五個jar包。不要企圖使用目錄,使用目錄仍然不識別。所以添加五個jar包的java指令確實很長。唯一欣慰的是,指令最后成功了,沒有錯誤。不過請注意一點:由上述的分析我們知道它只是不報驅動錯誤了,以后我們使用數據庫,它無法讀取識別數據庫的類型又是另外一碼事。(下面有兩張圖,第一個圖第一個指令是展示企圖使用目錄添加classpath是不正確的,第一個圖第二個指令表明添加具體某一個jar包,成功減少了錯誤。第二個圖第二個指令,一口氣寫了五個jar包,最后是成功的零錯誤,正確通過!。)


(三)WEKA使用的准備工作
WEKA數據格式中的數據屬性
-
數據格式中的數據屬性
1)二元屬性(定性的)
二元屬性的英文為"binary attribute",比較好理解。可以認為它是一種特殊的標稱屬性,只有兩個類別或狀態:0或1,其中0通常表示該屬性不出現,而1表示出現。二元屬性又稱布爾屬性,如果兩種狀態對應於true和false的話。
2)標稱屬性(定性的)
標稱屬性的英文為"nominal attribute",有時候感到不好理解。標稱屬性值既不是具有有意義的順序,也不是定量的。每個值代表某種類別、編碼或狀態,因此標稱屬性又被看做是分類的(categorical),所以有時候看到顧客的ID或者鞋碼尺寸SIZE等,總會誤認為是序號屬性。所以標稱屬性要抓住核心概念:標稱只是一種分類,即標稱數值的數學運算毫無意義。顧客ID有時候會是數值屬性,但注意應用的時候,如果顧客ID之間的數值加減乘除沒有任何意義,則我們不認為它是數值屬性,認為其為標稱屬性更為恰當。以年齡為例,年齡之間常常涉及到加減,所以年齡總認為是數值屬性,鞋碼大小不太涉及到加減意義,故認為標稱的。
3)數值屬性(定量的)
數值屬性的英文為"numericattribute",它是定量的,它是可度量的量。抓住這個概念,也就是說它的數值是有意義的,可以參與加減乘除運算的,其值進行數學運算的結果常常是有意義的。
4)序數屬性(定性的)
序數屬性的英文單詞為"ordinal attribute",也是不太好理解的一個屬性。但是切記序數屬性可以通過數值屬性離散化得到,並且序數屬性往往攜帶一種重要意義,就是具有有意義的先后次序。以快餐店售賣可樂大小杯為例,0表示小杯,1表示中杯,2表示大杯。它們既可以認為是序數屬性,也可以認為是間接的標稱屬性。或以顧客滿意度調查為例,0是很不滿意,1是不太滿意,2是一般,3是滿意,4是非常滿意。
WEKA數據集格式屬性
-
數據集格式問題
WEKA可識別ARFF格式,也可識別CSV電子數據表格式。雖然我們知道WEKA-Explorer對於兩者格式都能夠讀取,但對於CSV格式與ARFF格式比較感興趣。
1)CSV和ARFF格式
這里寫出CSV的格式,以weather.csv為例,其內容如下表所示。至於其arff格式的規范,我們通過看后續部分,就發現兩者實際上通過手動改寫。ARFF格式實際上就是統計了CSV格式的屬性分類,並加強規范。
| outlook,temperature,humidity,windy,play sunny,85,85,FALSE,no sunny,80,90,TRUE,no overcast,83,86,FALSE,yes rainy,70,96,FALSE,yes rainy,68,80,FALSE,yes rainy,65,70,TRUE,no overcast,64,65,TRUE,yes sunny,72,95,FALSE,no sunny,69,70,FALSE,yes rainy,75,80,FALSE,yes sunny,75,70,TRUE,yes overcast,72,90,TRUE,yes overcast,81,75,FALSE,yes rainy,71,91,TRUE,no |
2)兩者轉化
一、我們知道WEKA讀取時會自動轉化
二、如果需要人為干預,需要處理兩個格式的內容,那么這就是比較有意思的事情了
ARFF文件是Weka默認的儲存數據集文件。每個ARFF文件對應一個二維表格。表格的各行是數據集的各實例,各列是數據集的各個屬性。
下面是Weka自帶的"weather.arff"文件,在Weka安裝目錄的"data"子目錄下可以找到。 需要注意的是,在Windows記事本打開這個文件時,可能會因為回車符定義不一致而導致分行不正常。推薦使用UltraEdit這樣的字符編輯軟件察看ARFF文件的內容。
| %ARFF file for the weather data with some numric features % @relation weather @attribute outlook {sunny, overcast, rainy} @attribute temperature real @attribute humidity real @attribute windy {TRUE, FALSE} @attribute play {yes, no} @data % % 14 instances % sunny,85,85,FALSE,no sunny,80,90,TRUE,no overcast,83,86,FALSE,yes rainy,70,96,FALSE,yes rainy,68,80,FALSE,yes rainy,65,70,TRUE,no overcast,64,65,TRUE,yes sunny,72,95,FALSE,no sunny,69,70,FALSE,yes rainy,75,80,FALSE,yes sunny,75,70,TRUE,yes overcast,72,90,TRUE,yes overcast,81,75,FALSE,yes rainy,71,91,TRUE,no |
識別ARFF文件的重要依據是分行,因此不能在這種文件里隨意的斷行。空行(或全是空格的行)將被忽略。
以"%"開始的行是注釋,WEKA將忽略這些行。如果你看到的"weather.arff"文件多了或少了些"%"開始的行,是沒有影響的。
除去注釋后,整個ARFF文件可以分為兩個部分。第一部分給出了頭信息(Head information),包括了對關系的聲明和對屬性的聲明。第二部分給出了數據信息(Data information),即數據集中給出的數據。雖然Weka也支持其他一些格式的文件,但是ARFF格式是支持的最好的。因此有必要在數據處理之前把CSV數據集的格式轉換成ARFF。
將CSV轉換為ARFF最迅捷的辦法是使用WEKA所帶的命令行工具。運行WEKA的主程序,在菜單中找到"Simple CLI"模塊,它可提供命令行功能。在新窗口的最下方(上方是不能寫字的)的輸入框寫上
java weka.core.converters.CSVLoader filename.csv > filename.arff
即可完成轉換。
在WEKA 3.6中提供了一個"Arff Viewer"模塊(在Tools里),我們可以用它打開一個CSV文件將進行瀏覽,然后另存為ARFF文件。 進入"Exploer"模塊,從上方的按鈕中打開CSV文件然后另存為ARFF文件亦可。
Excel的XLS文件可以讓多個二維表格放到不同的工作表(Sheet)中,我們只能把每個工作表存成不同的CSV文件。打開一個XLS文件並切換到需要轉換的工作表,另存為CSV類型,點"確定"、"是"忽略提示即可完成操作。接下來把得到的CSV文件按照前述步驟轉換為ARFF即可。
WEKA分類挖掘的評估標准
-
分類挖掘算法的評估性能標准
Confusion Matrix是混淆矩陣,提供的評估標准,如下表所示。
| Predicted |
C1 |
¬ C1 |
ALL |
|
| Actual |
C1 |
True Positives (TP) |
False Negatives (FN) |
P |
| ¬ C1 |
False Positives (FP) |
True Negatives (TN) |
N |
1)TP/FN/FP/TN
對於C1事件來說,可將其看成事件中的正面事件,或稱為真事件,或稱為陽事件。由於預測結果為C1事件,既然結果為陽事件,正事件。說明實際過程可能由正到正,由負到正,都可成為真正,假正,說明預測都是正的。故TP可稱為判正得正或真陽率或真正率,FP則為判負為正或假陽率或假正率。同理FN可推理成為判正得負或真陰率或真反率,TN順理為判負得負或真陰率或假反率。
2)各評估率
准確率(識別率)
召回率(靈敏度)
精度
3)ROC曲線
反映的是分類器的真正例率(TPR,靈敏度)和假正例率(FPR)之間的權衡,簡而言之就說觀察"真判正"占據"判真"的比率。如下圖所示。

WEKA軟件Explorer之Classifier
-
Classifier菜單面板區域模塊介紹
1)Classifier
點擊choose按鈕,可以選擇weka提供的分類器。常用的分類器有:
a)bayes下的Naïve Bayes(朴素貝葉斯)和BayesNet(貝葉斯信念網絡)。
b)functions下的LibLinear、LibSVM(這兩個需要安裝擴展包)、Logistic Regression、Linear Regression。
c)lazy下的IB1(1-NN)和IBK(KNN)。
d)meta下的很多boosting和bagging分類器,比如AdaBoostM1。
e)trees下的J48(weka版的C4.5)、RandomForest。
2)Test options
評價模型效果的方法,有四個選項。
a)Use training set:使用訓練集,即訓練集和測試集使用同一份數據,一般不使用這種方法。
b)Supplied test set:設置測試集,可以使用本地文件或者url,測試文件的格式需要跟訓練文件格式一致。
c)Cross-validation:交叉驗證,很常見的驗證方法。N-folds cross-validation是指,將訓練集分為N份,使用N-1份做訓練,使用1份做測試,如此循環N次,最后整體計算結果。
d)Percentage split:按照一定比例,將訓練集分為兩份,一份做訓練,一份做測試。在這些驗證方法的下面,有一個More options選項,可以設置一些模型輸出,模型驗證的參數。
3)Result list
這個區域保存分類實驗的歷史,右鍵點擊記錄,可以看到很多選項。常用的有保存或加載模型以及可視化的一些選項。
4)Classifier output
分類器的輸出結果,默認的輸出選項有Run information,該項給出了特征、樣本及模型驗證的一些概要信息;Classifier model,給出的是模型的一些參數,不同的分類器給出的信息不同。最下面是模型驗證的結果,給出了 一些常用的一些驗證標准的結果,比如准確率(Precision),召回率(Recall),真陽性率(True positive rate),假陽性率(False positive rate),F值(F-Measure),Roc面積(Roc Area)等。混淆矩陣(Confusion Matrix)給出了測試樣本的分類情況,通過它,可以很方便地看出正確分類或錯誤分類的某一類樣本的數量。
一種舊版的WEKA軟件
-
WEKA舊版
1)背景
為什么要用舊版?
舊版由於受眾廣,新版雖然新,但API的接口修改沒有那么廣泛。此舊版WEKA增加新版WEKA所沒有的功能,受眾修改的程度也較為廣泛,如增加ID3可視化樹的功能。此版本來自於伍斯特理工學院。軟件下載網址如下:
http://davis.wpi.edu/~xmdv/weka/
2)安裝
下載舊版的WEKA后,安裝包的情況如下圖所示。

然而直接運行RunWeka是可以成功的,界面如下:

然而有一個問題,就是點擊該舊版Explorer按鈕后,沒有任何反應或響應。沒有反應的原因,是因為沒有GL4Java,如果想運行Explorer,必須安裝它(舊版網址也強調了該環境)。
那么在如下網址尋找一個插件GL4Java:
http://jausoft.com/Files/Java/1.1.X/GL4Java/binpkg/
GL4Java-Installer里面有主安裝程序,然而主安裝程序需要一些插件輔助安裝
插件的輔助清單如下圖紅色框內標注所示:

將輔助的插件下載並保存與舊版WEKA同一目錄的文件夾內,命名為"binpkg",文件夾內的目錄結構如下圖所示,binpkg文件夾內放置都是輔助插件:


然后點擊GL4Java內的install.bat,就可以正常安裝了,安裝界面如下:
切記選中自己安裝JDK的位置,然后點擊Start即可。
注意:RunWeka.bat和install.bat的點擊時不要以管理員身份運行,一旦以管理員身份運行,反而控制台出不來,所以直接雙擊就好。
3)測試
在打開舊版的WEKA后,點擊Explorer,可以出現Explorer界面。

參看分類器挖掘時,我們在使用ID3分類時,可視化樹的按鈕是灰色的。而使用舊版WEKA后,可以看到可視化樹的按鈕是可以點擊的。如下圖所示。

(四)WEKA實驗1:分類挖掘
決策樹分類挖掘
-
實驗目的
利用weka提供的決策樹算法,對提供的bank-data.csv數據進行決策樹分類挖掘。作業需完成以下內容:
(1)了解weka提供的數據預處理功能,去除bank-data數據中的無用屬性(如ID)結構,並對income屬性進行離散化處理;
(2)利用weka提供的多個決策樹歸納算法,構建決策樹模型(最后一個屬性pep作為需要預測的屬性),比較不同算法所構建模型的差別;
(3)對各種決策胡算法的預測結果進行分析,並比較分類性能。
-
實驗過程
1)數據預處理
a)數據讀取
讀取bank-data.csv格式數據,如下圖所示。

其中bank-data數據內容呈現出來,如下圖所示。

b)刪除無用參數
刪除無用參數ID,如下圖所示。

另存為ARFF格式,並用UltraEdit打開,觀察數據格式,如下圖所示。

根據ARFF數據格式,展示該數據的屬性如下。
| @attribute age numeric @attribute sex {FEMALE,MALE} @attribute region {INNER_CITY,TOWN,RURAL,SUBURBAN} @attribute income numeric @attribute married {NO,YES} @attribute children numeric @attribute car {NO,YES} @attribute save_act {NO,YES} @attribute current_act {NO,YES} @attribute mortgage {NO,YES} @attribute pep {YES,NO} |
其中{YES,NO}是二元屬性,{INNER_CITY,TOWN,RURAL,SUBURBAN}是標稱屬性,至於{FEMALE,MALE}是標稱屬性或二元屬性,而numeric認為是數值屬性。
說句實話,這些屬性初學時基本分不清,還要對照課件才能分清楚,甚至有時對照課件都會分不清楚。所以覺得不能脫離英文環境,必須要結合英文,加強理解。可參見WEKA的數據准備里相關知識。
c)修改部分參數的數據屬性
我們做一些簡單修改,通過觀察WEKA中某一參數的分布,如children個數,如下圖所示。

children的取值分布為離散,WEKA軟件顯示children的Type是Numeric。我們觀察直方圖,children取值為0,1,2,3中的一個。我們修改ARFF格式數據,將
@attribute children numeric
修改為(請注意區分全角半角的符號)
@attribute children {0,1,2,3}
重新使用WEKA讀入,再點擊children參數,發現其Type更改為Nominal。如下圖所示。

這說明了一點,那就是標稱屬性與數值屬性在應用時可能沒有明顯的界限,只要數據屬性服務於我們的要求就行。
d)離散化income參數
選中income,然后選擇Filter-unsupervised-attribute-Discretize,然后進行后續操作,如下圖所示。

修改income的離散化參數,雙擊choose按鈕旁的命令欄,彈出weka.gui.GenericObjectEditor對話框,最上方兩行分別是取值區間,分成幾類。修改的參數如下圖所示。

點擊OK,再在命令欄右邊的按鈕,為保證對照對比income的分類情況,我們先觀察不應用離散處理之前的情形,如下圖所示。

點擊Apply,對income應用離散化后的生成數據情形,如下圖所示,顯然將income參數分成了三部分。

我們發現一個有趣的事實,那就是age參數也被分成了三份,如下圖所示。

我猜測可能是因為在屬性里面,由於我們剛剛修改了children,僅剩下age和income兩個參數沒有改動,且仍然保持numeric類型,應用離散化時,結果把這兩個都分成了三類。說明有兩個操作無效:一是只要符合離散化條件的,weka都做離散化,不管你選不選中income;二是對已有標稱數值的屬性作離散化處理是無效的。
2)各分類挖掘算法
a)J48分類器
打開classifier,點擊choose,在分類算法中選擇trees-J48,如下圖所示。

選擇完J48分類器后,返回到classifier界面,在TestOptions內勾選交叉驗證(Cross-validation),旁邊的默認值10指的是十折交叉驗證。在MoreOptions中勾選Output predictions。參數設置如下圖所示。

點擊Start,啟動實驗,在右側的classifier ouput內我們可以看到J48分類器的分類實驗結果。如下圖所示。

3)各分類器結果分析
a)J48分類器
-
先給出一些文字或數字的結果
本分類器的分類驗證結果,如下圖所示。

通過右鍵點擊實驗記錄時間點,選擇點擊Visualize Tree,可以生成可視化生成樹,點擊操作如下圖所示。

由下圖可知,這是預測結果的數據,可以看到每個樣本的實際分類,預測分類,預測概率以及是否錯分等信息。

其中我們將分類結果文字清晰地呈現出來,進行分析。如下表所示。
| === Stratified cross-validation === === Summary === Correctly Classified Instances 510 85% Incorrectly Classified Instances 90 15% Kappa statistic 0.6983 Mean absolute error 0.2209 Root mean squared error 0.3518 Relative absolute error 44.5145% Root relative squared error 70.6212% Total Number of Instances 600 === Detailed Accuracy By Class === TP Rate FP Rate Precision Recall F-Measure ROC Area Class 0.847 0.147 0.829 0.847 0.838 0.862 YES 0.853 0.153 0.869 0.853 0.861 0.862 NO Weighted Avg. 0.85 0.151 0.85 0.85 0.85 0.862 === Confusion Matrix === a b <-- classified as 232 42 | a = YES 48 278 | b = NO |
-
可視化結果
生成的可視化樹,由於節點或分類參數較多,部分節點無法看清,但總體脈絡可以如下圖所示。

Explorer的Visualize面板,可以觀察兩兩特征之間的樣本點分布情形,如下圖所示。

隨便選一個區域,例如選擇pep和age交叉對應的區域,點擊后會出現新的圖框,以展示pep特征與age特征之間的關系。如下圖所示。

回歸到Classifier面板下的ResultLists右鍵選擇實驗記錄點,然后選擇點擊Visualize Classify Errors,如下圖所示。

選擇Visualize Classify Errors此項參數,然后會生成可視化結果,這個結果實際上對應的是可視化的混淆矩陣圖示,也就是可以展示誤判概率點(正判反,反判正),正確判別概率點(正判正,反判反),圖像中十字點表示對判,方形點表示誤判,X軸表示實際類別,Y軸表示預測類別。如下圖所示。把鼠標放到點上,點擊后也能跳出特征點的具體值,方便觀察和研究特征點離群原因或不符合正常結論的原因。

同理我們再選取讀取Visualize threshold curve參數,選擇YES(正類),如下圖所示。

Visualize threshold curve參數顯示的是分類置信度在不同閾值下,分類效果評價標准的對比情況,其中就包括ROC曲線(縱軸為TP,橫軸為FP),如下圖所示。

b)ID3分類器
-
先給出一些文字或數字的結果
下面給出ID3的分類文字結果。
| === Stratified cross-validation === === Summary === Correctly Classified Instances 463 77.1667 % Incorrectly Classified Instances 123 20.5 % Kappa statistic 0.5789 Mean absolute error 0.2112 Root mean squared error 0.4536 Relative absolute error 43.5901 % Root relative squared error 92.1819 % UnClassified Instances 14 2.3333 % Total Number of Instances 600 === Detailed Accuracy By Class === TP Rate FP Rate Precision Recall F-Measure ROC Area Class 0.801 0.219 0.753 0.801 0.776 0.784 YES 0.781 0.199 0.825 0.781 0.803 0.79 NO Weighted Avg. 0.79 0.208 0.792 0.79 0.79 0.787 === Confusion Matrix === a b <-- classified as 213 53 | a = YES 70 250 | b = NO |
下面給出ID3的分類結果的圖片示意。

-
可視化結果
生成的可視化樹步驟中發現無法生成ID3分類器,說明WEKA原版軟件沒有提供ID3的可視化樹,故我使用了舊版WEKA,得到了相關的可視化樹。
(圖片這么長的原因,一是分類樹有些寬大,二是我對筆記本電腦使用了擴展屏幕截屏所致。)

至於特征之間的關系,如下圖所示。

我們選擇Age和Income之間的關系進行分析,如下圖所示。可以觀察到年齡越小,收入越高的樣本基本不存在,密集程度較高的是年齡又小收入又少的情形。

其ROC曲線可以如圖所示:

c)NBTree分類器
-
先給出一些文字或數字的結果
NBTree是Naive Bayesian分類樹,下面給出分類文字結果。
| === Stratified cross-validation === === Summary === Correctly Classified Instances 502 83.6667 % Incorrectly Classified Instances 98 16.3333 % Kappa statistic 0.6697 Mean absolute error 0.2165 Root mean squared error 0.3592 Relative absolute error 43.6251 % Root relative squared error 72.1096 % Total Number of Instances 600 === Detailed Accuracy By Class === TP Rate FP Rate Precision Recall F-Measure ROC Area Class 0.799 0.132 0.836 0.799 0.817 0.879 YES 0.868 0.201 0.837 0.868 0.852 0.879 NO Weighted Avg. 0.837 0.169 0.837 0.837 0.836 0.879 === Confusion Matrix === a b <-- classified as 219 55 | a = YES 43 283 | b = NO |
在運行狀態中,出現明顯的停滯和計算停留,說明該分類樹收斂慢,計算速度中等,占用一定時間,沒有J48分類快速。
-
可視化結果
在這里我們簡單展示NBTree的可視化樹

其ROC曲線可以如下所示:

d)分析
由於篇幅過長,這里不再列舉其他幾類分類樹的全部過程。我還測試了隨機樹Random Tree等等其他分類器的分類效果,測試狀態如圖所示。

在上述三種方法中,由數據分析我可以看到J48的分類效果最好,達到85%,其次是貝葉斯分類,達到83.7%,最差是ID3分類僅有77.1%。
而在WEKA提供的所有方法中,面對bank數據下,我將所有分類器的結果列舉成表,以供閱覽:
| Method Name |
Correctly |
InCorrectly |
TP/FN/FP/TN |
BuildModelTIme |
| J48Graft |
85.0000 % |
15.0000 % |
232/42/48/278 |
0.05 sec |
| J48 |
85.0000 % |
15.0000 % |
232/42/48/278 |
0.18 sec |
| LMT |
84.1667 % |
15.8333 % |
222/52/43/283 |
16.19 sec |
| NBTree |
83.6667 % |
16.3333 % |
219/55/43/283 |
7.3 sec |
| REPTree |
83.5000 % |
16.5000 % |
229/45/54/272 |
0.02 sec |
| SimpleCart |
82.3333 % |
17.6667 % |
213/61/45/281 |
2.04 sec |
| LADTree |
81.5000 % |
18.5000 % |
201/73/38/288 |
0.47 sec |
| ADTree |
80.5000 % |
19.5000 % |
192/82/35/291 |
0.19 sec |
| BFTree |
80.3333 % |
19.6667 % |
217/57/61/265 |
2.07 sec |
| RandomForest |
79.8333 % |
20.1667 % |
218/56/65/261 |
0.04 sec |
| ID3 |
77.1667 % |
20.5000 % |
213/53/70/250 |
0.13 sec |
| RandomTree |
74.8333 % |
25.1667 % |
200/74/77/249 |
0.07 sec |
| FT |
72.5000 % |
27.5000 % |
190/84/81/245 |
2.33 sec |
| DecisionStump |
68.5000 % |
31.5000 % |
110/164/25/301 |
0.00 sec |
解釋部分算法:DecisionStump是單層決策樹算法,常被作為boosting的基本學習器。LMT是組合樹結構和Logistic回歸模型,每個葉子節點是一個Logistic回歸模型,准確性比單獨的決策樹和Logistic回歸方法要好。NBTree是貝葉斯分類法。ADTree是一種基於boosting的決策樹學習算法,其預測准確率比一般決策樹高,並可以給出預測置信度,實際中常使用它的改良BICA算法。
由觀察可知,J48的分類性能最好。時間收斂快,分類准確。除REPTree外,其余識別率達到80%以上的算法,收斂時間均比J48要長,分類准確性略遜一籌。值得敬佩J48的繼承性與改進性,也向ID3致以敬意,沒有ID3的優點,C4.5也不會繼承,C4.5的信息增益率和剪枝策略是加強收斂與提高准確的重要手段。
垃圾郵件過濾
-
實驗目的
利用weka提供的文本分類器實現英文垃圾郵件的過濾,需要完成內容如下:
(1)了解垃圾郵件過濾基本思想;
(2)利用weka的預處理功能將文本轉化為向量;
(3)利用weka提供的分類算法實現垃圾郵件的過濾,最少使用兩種分類器;
(4)分析分類結果,並對分類器的性能進行比較。實驗過程
小知識:1978年5月3日,星期六。網絡時代第一批垃圾郵件,由美國DEC公司通過互聯網的前身阿帕網發送給了393個接收者。垃圾郵件,指的是不請自來、強行塞入信箱的郵件。在若干年前,郵箱存儲空間普遍很小,垃圾郵件能夠塞爆郵箱,讓人們無法工作。
-
實驗過程
簡要分析垃圾郵件過濾的實驗過程。我的初步思路是將語料庫轉化為向量,然后分析向量模式,過濾敏感詞。最后得到垃圾樣本的分類。那么這樣文縐縐的話如何實踐是個問題,可以說完全不知道。通過查閱互聯網,明白其中含義。
語料庫轉化為向量,無非是將語料庫的文字轉化為arff格式的可讀文本,並且要將文本分詞處理,去標點處理等等,否則不能讀入WEKA進行后續過濾。
分析向量模式,無非是用WEKA分類器去做分析。
1)數據預處理
a)文本數據讀入
我們既可以自己使用Java編程或Python編程,將語料庫轉換為Arff格式,並做相關處理。也可以使用WEKA的接口,其提供了文本處理的一條龍服務,堪稱業內楷模。
其命令行接口在Simple CLI按鈕彈出的界面內,如下圖所示:


我們使用WEKA的兩類功能
TextDirectoryLoader
StringToWordVector
上者具有的功能為將文本轉換為ARFF格式,下者具有的功能為將文本分詞,去標點處理等。
首先將文本語料以如下目錄結構放置,否則WEKA命令行處理失敗:

其次,值得一提bare是老師所給語料庫的文件名,以及二級目錄與末端文件都是老師所給的文件名,也可以根據自己需要隨意重命名,只要目錄關系正確就行。我的語料庫全部放在test文件夾內,所以我的執行命令語句如下,並如圖所示:
java weka.core.converters.TextDirectoryLoader -dir test > all_text_examples.arff

我們可以打開此生成ARFF文件,內容如下圖所示:

觀察可知,WEKA將文本語料庫讀入后,將每個Part的文件夾內的所有文本歸為每一類class。每一類的class內的文本有多個,接下來我們要對每個class內的每將文本作切割處理分詞。
接下來,使用WEKA讀入文本數據,每個文件夾內的文本數量狀態可以如下圖所示:

b)注意事項,避免歧途
上面a)文本數據讀入,觀察其中的圖片,可以看到文本讀入后的類有part1至part7。請注意,這樣的part個數是不對的!這並不是說目錄結構不對,而是說文本過濾訓練的模型建模的不對!
既然要訓練模型分為"不是垃圾郵件(NO)"和"是垃圾郵件(YES)",那么我們的訓練集當然在訓練之處,就僅分為兩個類別才對。即把垃圾郵件的文本整合在一個文件夾內,不是垃圾郵件的文本整合在另一個文件夾內,總共才兩個文件夾,而不是之前part1至part7的七個文件夾。
只有分成兩個文件夾去訓練,這才是正確的分類結果。如果是part1至part7這樣的設置,到最后訓練出來的模型效果非常差。指標Relative absolute error大約高達98%,說明根本就沒有分類。所以在訓練集上,務必就分成兩個類,一個類全是正常郵件,另一個類全是垃圾郵件。如下圖所示:

c)文本向量化操作
然后選擇Filter內unsupervised-attribute-StringToWordVector方法,操作如圖所示:

這里簡要介紹一下StringToWordVector可能需要自己做調整的參數:
-W 需要保留的單詞個數,默認為1000。這不是最終的特征維數,但是維數跟此參數是正相關的
-stopwords <file> 輸入停詞文件,文件格式為每一個詞一行。在讀文件到轉化特征時會自動去掉這些常用詞,系統自帶有一套停用詞。
-tokenizer <spec> 自定義所要去除的符號,一般為標點符號。默認有常用的標點符號,但往往是不夠的,所以需自己添加
其他參數只需默認值即可,可以按照圖中紅框的顯示值修改好,可如圖所示修改。
在GUI當中,還有一些參數設置需要介紹:lowerCaseTokens是否區分大小寫,默認為false不區分,這里一般要設置為ture,因為同一個詞就會有大小寫的區別。至此文本便被分離成向量化格式。正確的向量化文本應當如下圖所示:
我們注意到文本向量化的結構大致為如下,下面使用偽代碼進行示意向量化文本的內容:
@attribute @@class@@ {msg,spam}
@attribute WORD1 numeric
@attribute WORD2 numeric
@attribute WORD3 numeric
@attribute WORD4 numeric
{1 0.693147,2 1.098612,4 1.386294,……,}
{4 2.70805 ,7 1.098612,8 2.639057,……,}
{0 spam,8 4.672829,9 1.791759,13 1.791759,……,}
{0 spam,2 3.218876,4 1.386294,8 2.397895,……,}
這里class的標簽放在第一列。spam代表垃圾郵件,而沒有標出標簽的代表正常郵件,即msg。為什么spam標記出來而msg沒有,這說明WEKA的命令一脈相承,即使沒有出現msg標簽,也可以繼續投入使用。
2)垃圾郵件分類訓練
a)IBk懶惰分類器
懶惰分類器顧名思義,該算法不需要太操心,投入即可使用,參數使用起來方便,參數設定不需要太專業,自然效果一般。IBk本質上就是距離為k的k最近鄰分類器。IB1顯然就是距離為1的k近鄰分類,距離為1說明只有一個鄰居,也就是說IB1總共分成兩個類。選擇IBk該分類器的流程如下圖所示:

由於該垃圾郵件的結論具有二值性,要么是垃圾郵件,要么不是垃圾郵件。故在kNN個數上設置為1,也就是說總共分成兩個類進行訓練。交叉驗證設為三份,便於訓練快速。如下圖所示:

於是,最后一步,切記將分析對象調整至class標簽,否則分類不正確。調整對象的操作流程可下圖所示。

最后,我們的圖片結果如下:

文字結果摘錄如下:
| === Stratified cross-validation === === Summary === Correctly Classified Instances 1942 88.9194 % Incorrectly Classified Instances 242 11.0806 % Kappa statistic 0.5221 Mean absolute error 0.1113 Root mean squared error 0.3326 Relative absolute error 42.6065 % Root relative squared error 92.0846 % Total Number of Instances 2184 === Detailed Accuracy By Class === TP Rate FP Rate Precision Recall F-Measure ROC Area Class 0.959 0.496 0.914 0.959 0.936 0.733 msg 0.504 0.041 0.694 0.504 0.584 0.733 spam Weighted Avg. 0.889 0.425 0.88 0.889 0.882 0.733 === Confusion Matrix === a b <-- classified as 1772 75 | a = msg 167 170 | b = spam |
觀察分類效果,正確分類達到88.9%,這個結果較為不錯,可以接受,可以認為分類訓練較為合理。觀察精度precision可知,平均為88%,查全率大約為89%。總體效果良好。
b)聚類分析之SimpleKMeans
我們使用聚類來做一次分析,請注意垃圾的過濾模型需要分類挖掘,而不是聚類。這里做聚類僅僅是觀察樣本的區分程度。
選擇SimpleKMeans聚類,將聚類簇數設為兩個,並將聚類分析對象設定為class標簽,最后生成結果如下:

文字結果大致如下:
| Number of iterations: 11 Within cluster sum of squared errors: 43032.64174958714 === Model and evaluation on training set === Clustered Instances 0 464 ( 21%) 1 1720 ( 79%) Class attribute: @@class@@ Classes to Clusters: 0 1 <-- assigned to cluster 388 1459 | msg 76 261 | spam Cluster 0 <-- spam Cluster 1 <-- msg Incorrectly clustered instances : 649.0 29.7161 % |
由此觀察可知,迭代11次后聚成兩類,聚類失敗的樣本大約為29%,即有71%的樣本可以成功聚類,在這71%的可聚在一起的樣本中又有其中的79%聚成1號類,其中的21%聚成0號類。
(五)WEKA實驗2:聚類分析
聚類分析
-
實驗目的
利用weka提供的k-means算法,對提供的bank-data.csv數據進行決策樹分類挖掘。作業需完成以下內容:
(1)了解weka提供的數據預處理功能,去除bank-data數據中的無用屬性(如ID)結構,並對income屬性進行離散化處理;
(2)利用weka提供的k-means算法,進行聚類分析,k選不同取值,查看並比較聚類結果;
(3)選擇不同的距離函數,重新進行聚類分析,並比較聚類結果。
1)數據預處理
數據預處理工作和之前在分類挖掘相似。
2)K-Means聚類算法
a)選擇Cluster
我們對bank數據作聚類分析,選擇K-Means算法。操作選項如圖所示:

b)設置聚類參數
選擇完畢后,需要對K-Means聚類設置相關參數,主要的設置區域如圖所示:

顯然參數設置的不同,為聚類帶來的效果也不同。
主要的參數為NumCluster和Seed。
前者為聚類簇數,后者為種子數。
c)聚成2類的結果
設置聚成2類之后,生成結果如下:

文字結果大致摘錄如下:
| Number of iterations: 3 Within cluster sum of squared errors: 1737.4061909284878 Time taken to build model (full training data) : 0.24 seconds === Model and evaluation on training set === Clustered Instances 0 252 ( 42%) 1 348 ( 58%) Class attribute: pep Classes to Clusters: 0 1 <-- assigned to cluster 121 153 | YES 131 195 | NO Cluster 0 <-- YES Cluster 1 <-- NO Incorrectly clustered instances : 284.0 47.3333 % |
d)聚成3類的結果
假設我們將簇數取為3,隨機數為10。生成結果如下:

以及主要指標結果:

可以觀察其可視化結果,如圖所示:
展示年齡與pep的關系

展示收入與pep的關系

展示地域與pep的關系

由上述幾幅圖我們可以看出聚類效果並不好,在簇點下聚類點顏色混雜度較高,區分度不好,辨析度不強,不能有效挖掘關系。其重要判定指標文字結果如下:
| Class attribute: pep Classes to Clusters: 0 1 2 <-- assigned to cluster 114 90 70 | YES 125 125 76 | NO Cluster 0 <-- YES Cluster 1 <-- NO Cluster 2 <-- No class Incorrectly clustered instances : 361.0 60.1667 % |
e)調整參數
調整參數及生成結果如下
| 參數 |
NumCluster |
Seed |
squared errors |
Incorrectly instances |
| 2 |
2 |
10 |
1737.4 |
284.0 47.3333 % |
| 2 |
20 |
1723.0 |
294.0 49 % |
|
| 2 |
30 |
1778.3 |
261.0 43.5 % |
|
| 2 |
40 |
1752.7 |
272.0 45.3333 % |
|
| 2 |
50 |
1756.1 |
259.0 43.1667 % |
|
| 2 |
100 |
1773.0 |
271.0 45.1667 % |
|
| 3 |
3 |
10 |
2143.0 |
361.0 60.1667 % |
| 3 |
20 |
2121.0 |
378.0 63 % |
|
| 3 |
30 |
2168.0 |
340.0 56.6667 % |
|
| 3 |
40 |
2166.0 |
341.0 56.8333 % |
|
| 3 |
50 |
2159.0 |
319.0 53.1667 % |
|
| 3 |
100 |
2151.0 |
295.0 49.1667 % |
|
| 4 |
4 |
10 |
1991.0 |
416.0 69.3333 % |
| 4 |
20 |
2018.0 |
400.0 66.6667 % |
|
| 4 |
30 |
2118.0 |
353.0 58.8333 % |
|
| 4 |
40 |
2071.0 |
393.0 65.5 % |
|
| 4 |
50 |
2069.0 |
336.0 56 % |
|
| 4 |
100 |
2046.0 |
349.0 58.1667 % |
|
| 5 |
5 |
10 |
1916 |
441.0 73.5 % |
| 5 |
20 |
1935.0 |
416.0 69.3333 % |
|
| 5 |
30 |
1976.0 |
388.0 64.6667 % |
|
| 5 |
40 |
1925.0 |
426.0 71 % |
|
| 5 |
50 |
1956.0 |
378.0 63 % |
|
| 5 |
100 |
1971.0 |
374.0 62.3333 % |
|
| 6 |
6 |
10 |
1889.0 |
441.0 73.5 % |
| 6 |
20 |
1864 |
414.0 69 % |
|
| 6 |
30 |
1899.0 |
407.0 67.8333 % |
|
| 6 |
40 |
1914.0 |
434.0 72.3333 % |
|
| 6 |
50 |
1887.0 |
396.0 66 % |
|
| 6 |
100 |
1864.0 |
401.0 66.8333 % |
|
| 7 |
7 |
10 |
1805.0 |
472.0 78.6667 % |
| 7 |
20 |
1832 |
419.0 69.8333 % |
|
| 7 |
30 |
1854.0 |
411.0 68.5 % |
|
| 7 |
40 |
1831.0 |
453.0 75.5 % |
|
| 7 |
50 |
1805.0 |
414.0 69 % |
|
| 7 |
100 |
1813.0 |
416.0 69.3333 % |
|
| 8 |
8 |
10 |
1807.0 |
473.0 78.8333 % |
| 8 |
20 |
1733 |
445.0 74.1667 % |
|
| 8 |
30 |
1756.0 |
447.0 74.5 % |
|
| 8 |
40 |
1702.0 |
459.0 76.5 % |
|
| 8 |
50 |
1689.0 |
433.0 72.1667 % |
|
| 8 |
100 |
1745.0 |
450.0 75 % |
|
| 9 |
9 |
10 |
1750.0 |
482.0 80.3333 % |
| 9 |
20 |
1688 |
454.0 75.6667 % |
|
| 9 |
30 |
1718.0 |
451.0 75.1667 % |
|
| 9 |
40 |
1681.0 |
476.0 79.3333 % |
|
| 9 |
50 |
1721.0 |
460.0 76.6667 % |
|
| 9 |
100 |
1657.0 |
465.0 77.5 % |
|
| 10 |
10 |
10 |
1655.0 |
497.0 82.8333 % |
| 10 |
20 |
1647.0 |
459.0 76.5 % |
|
| 10 |
30 |
1707.0 |
455.0 75.8333 % |
|
| 10 |
40 |
1599.0 |
495.0 82.5 % |
|
| 10 |
50 |
1608 |
469.0 78.1667 % |
|
| 10 |
100 |
1617.0 |
470.0 78.3333 % |
f)數據分析
在同一情況下,根據當前Within cluster sum of squared errors,調整"seed"參數,觀察Within cluster sum of squared errors(SSE)變化。采納SSE最小的一個結果。
結合實際情況,對於pep僅有YES,NO的兩個結果的事實,一般聚簇調整為2個。再在2個聚簇的條件內選擇合適的方差中心。
3)層次聚類
a)選擇層次聚類HierarchicalCluster
鑒於K-Means聚類的操作解釋足夠詳細,這里有詳有略地介紹實驗內容。如下圖展示選擇聚類方法:

在如下圖所示的按鈕中,點擊cluster的命令行欄,彈出通知,點選choose可以更換不同的距離函數。


b)層次聚類過程及結果
我們當前選擇歐式距離,選擇分析對象為Pep,再運行start。生成的圖片結果為:

文字摘錄結果如下:
| Time taken to build model (full training data) : 16.32 seconds === Model and evaluation on training set === Clustered Instances 0 599 (100%) 1 1 ( 0%) Class attribute: pep Classes to Clusters: 0 1 <-- assigned to cluster 274 0 | YES 325 1 | NO Cluster 0 <-- NO Cluster 1 <-- No class Incorrectly clustered instances : 275.0 45.8333 % |
對比K-Means聚類,KMeans聚類的參數結果如下。
KMeansIncorrectly clustered instances : 284 43.3333 %
那么再看看層次聚類的結果
0 1 <-- assigned to cluster
274 0 | YES 45.7%
325 1 | NO 54.3%
對比KMeans聚類
Clustered Instances
0 252 ( 42%)
1 348 ( 58%)
為什么這么對比,為什么層次聚類的其中某一個樣本單獨作為一個類。原因在於層次聚類的規則算法,詳情可參見書籍或互聯網。層次聚類的最后一次操作,當然為大集合類與最后一個類的合並過程。故WEKA的層次聚類停留在最后一次上。在對比標簽的類上,兩者聚類的效果近似,沒有明顯分別。
c)距離函數更改為切比雪夫距離
我們更換距離函數為切比雪夫距離公式,測試結果如圖所示。

文字結果摘錄如下。
| Time taken to build model (full training data) : 8.87 seconds === Model and evaluation on training set === Clustered Instances 0 1 ( 0%) 1 599 (100%) Class attribute: pep Classes to Clusters: 0 1 <-- assigned to cluster 1 273 | YES 0 326 | NO Cluster 0 <-- YES Cluster 1 <-- NO Incorrectly clustered instances : 273.0 45.5 % |
曼哈頓距離(Manhattan Distance)可以理解為一階范數距離,歐式距離(Euclidean Distance)可以理解為二階范數距離,切比雪夫距離(Chebyshev Distance)可以理解為無窮范數距離。故在更改選擇距離范數的策略上,可能不具有左右影響力,原因在於距離函數的公式彼此之間相差無幾。
4)K-Means與層次聚類比較
對於K-Means算法,其時間復雜度為O(tKmn),其中,t為迭代次數,K為簇的數目,m為數據記錄數,n為維數
空間復雜度為O((m+K)n),其中,K為簇的數目,m為數據記錄數,n為維數。
以實驗為例,在簇數為2的情形下分析bank數據,迭代次數為3次,數據記錄數大約為600條,維數為11維。時間復雜度大約為3萬,空間復雜度大約為6千。
對於層次聚類算法,層次聚類的空間復雜度O(m2)。總時間復雜度O(m2logm),其中m是數據記錄數。對於本實驗而言,空間復雜度大約為36萬,時間復雜度上大約近乎100萬。故顯然在運行時間上,理論分析結果是層次聚類運行時間更長。
根據WEKA分別運行兩算法的時間結果,對比可知如下:
| Name |
K-Means(歐氏,2類) |
層次聚類 |
| Time |
0.24 sec |
16.32 sec |
5)文本聚類
可參見垃圾郵件過濾中德聚類分析。
(六)WEKA實驗3:Apriori關聯挖掘
實驗目的
-
實驗目的
利用weka提供的關聯算法,實現給定數據對象的關聯挖掘。
WEKA關聯挖掘
1)數據預處理
首先對bank數據進行預處理,預處理的對象包括有"ID","income"和"children"。對於ID要remove,對於income使用離散化過濾,使其在1至4之間,形成三份。children則在文本文件中修改參數,詳細操作在實驗1中應該非常詳細,故不再贅述。
唯有處理掉所有Numeric數據,全部轉化為Nominal類型數據,才可以使用Apriori算法,否則該算法按鈕為灰色不可選狀態。
2)關聯挖掘
選擇Apriori算法的操作如圖所示。

對於Apriori的參數窗口,可以點擊命令行欄彈出。圖片內容如圖所示。

詳細的參數含義如下羅列:
| car 如果設為真,則會挖掘類關聯規則而不是全局關聯規則。 classindex 類屬性索引。如果設置為-1,最后的屬性被當做類屬性。 delta 以此數值為迭代遞減單位。不斷減小支持度直至達到最小支持度或產生了滿足數量要求的規則。 lowerBoundMinSupport 最小支持度下界。 metricType 度量類型。設置對規則進行排序的度量依據。可以是:置信度(類關聯規則只能用置信度挖掘),提升度(lift),杠桿率(leverage),確信度(conviction)。 在 Weka中設置了幾個類似置信度(confidence)的度量來衡量規則的關聯程度,它們分別是: a) Lift : P(A,B)/(P(A)P(B)) Lift=1時表示A和B獨立。這個數越大(>1),越表明A和B存在於一個購物籃中不是偶然現象,有較強的關聯度。 b)Leverage :P(A,B)-P(A)P(B) Leverage=0時A和B獨立,Leverage越大A和B的關系越密切。 c) Conviction:P(A)P(!B)/P(A,!B) (!B表示B沒有發生) Conviction也是用來衡量A和B的獨立性。從它和lift的關系(對B取反,代入Lift公式后求倒數)可以看出,這個值越大, A、B越關聯。 minMtric 度量的最小值。 numRules 要發現的規則數。 outputItemSets 如果設置為真,會在結果中輸出項集。 removeAllMissingCols 移除全部為缺省值的列。 significanceLevel 重要程度。重要性測試(僅用於置信度)。 upperBoundMinSupport 最小支持度上界。從這個值開始迭代減小最小支持度。 verbose 如果設置為真,則算法會以冗余模式運行。 |
根據實際情況,我們將參數設置為:
| % car I - 輸出項集,若設為false則該值缺省; % numRules N 10 - 規則數為10; % metricType T 0 – 度量單位選為置信度,(T1-提升度,T2杠桿率,T3確信度); % minMetric C 0.9 – 度量的最小值為0.9; % delta D 0.05 - 遞減迭代值為0.05; % upperBoundMinSupport U 1.0 - 最小支持度上界為1.0; % lowerBoundMinSupport M 0.5 - 最小支持度下界為0.5; % significanceLevel S -1.0 - 重要程度為-1.0; % classIndex c -1 - 類索引為-1輸出項集設為真 % (由於car, removeAllMissingCols, verbose都保持為默認值False,因此在結果的參數設置為缺省,若設為True,則會在結果的參數設置信息中分別表示為A, R,V) |
那么配置好參數,我們應用關聯挖掘算法於bank數據,結果如圖所示。

文字結果摘錄如下:
| Apriori ======= Minimum support: 0.1 (60 instances) Minimum metric <confidence>: 0.9 Number of cycles performed: 18
Generated sets of large itemsets: Size of set of large itemsets L(1): 28 Size of set of large itemsets L(2): 232 Size of set of large itemsets L(3): 524 Size of set of large itemsets L(4): 277 Size of set of large itemsets L(5): 33
Best rules found: 1. income='(43758.136667-inf)' 80 ==> save_act=YES 80 conf:(1) 2. age='(50.666667-inf)' income='(43758.136667-inf)' 76 ==> save_act=YES 76 conf:(1) 3. income='(43758.136667-inf)' current_act=YES 63 ==> save_act=YES 63 conf:(1) 4. age='(50.666667-inf)' income='(43758.136667-inf)' current_act=YES 61 ==> save_act=YES 61 conf:(1) 5. children=0 save_act=YES mortgage=NO pep=NO 74 ==> married=YES 73 conf:(0.99) 6. sex=FEMALE children=0 mortgage=NO pep=NO 64 ==> married=YES 63 conf:(0.98) 7. children=0 current_act=YES mortgage=NO pep=NO 82 ==> married=YES 80 conf:(0.98) 8. children=0 mortgage=NO pep=NO 107 ==> married=YES 104 conf:(0.97) 9. income='(43758.136667-inf)' current_act=YES 63 ==> age='(50.666667-inf)' 61 conf:(0.97) 10. income='(43758.136667-inf)' save_act=YES current_act=YES 63 ==> age='(50.666667-inf)' 61 conf:(0.97) |
3)調整參數
根據任務要求,調整MetricType參數,可以得到下表:
| Output |
|
| Lift |
Minimum support: 0.25 (150 instances) Minimum metric <lift>: 1.1 Number of cycles performed: 15 Generated sets of large itemsets: Size of set of large itemsets L(1): 21 Size of set of large itemsets L(2): 59 Size of set of large itemsets L(3): 16 Best rules found: 1. age='(-inf-34.333333]' 195 ==> income='(-inf-24386.173333]' 174 conf:(0.89) < lift:(1.88)> lev:(0.14) [81] conv:(4.65) 2. income='(-inf-24386.173333]' 285 ==> age='(-inf-34.333333]' 174 conf:(0.61) < lift:(1.88)> lev:(0.14) [81] conv:(1.72) 3. married=YES 396 ==> mortgage=NO pep=NO 171 conf:(0.43) < lift:(1.24)> lev:(0.06) [33] conv:(1.14) 4. mortgage=NO pep=NO 209 ==> married=YES 171 conf:(0.82) < lift:(1.24)> lev:(0.06) [33] conv:(1.82) 5. married=YES mortgage=NO 261 ==> pep=NO 171 conf:(0.66) < lift:(1.21)> lev:(0.05) [29] conv:(1.31) 6. pep=NO 326 ==> married=YES mortgage=NO 171 conf:(0.52) < lift:(1.21)> lev:(0.05) [29] conv:(1.18) 7. children=0 263 ==> pep=NO 167 conf:(0.63) < lift:(1.17)> lev:(0.04) [24] conv:(1.24) 8. pep=NO 326 ==> children=0 167 conf:(0.51) < lift:(1.17)> lev:(0.04) [24] conv:(1.14) 9. married=YES save_act=YES 277 ==> pep=NO 175 conf:(0.63) < lift:(1.16)> lev:(0.04) [24] conv:(1.23) 10. pep=NO 326 ==> married=YES save_act=YES 175 conf:(0.54) < lift:(1.16)> lev:(0.04) [24] conv:(1.15) |
| Leverage |
Minimum support: 0.1 (60 instances) Minimum metric <leverage>: 0.1 Number of cycles performed: 18 Generated sets of large itemsets: Size of set of large itemsets L(1): 28 Size of set of large itemsets L(2): 232 Size of set of large itemsets L(3): 524 Size of set of large itemsets L(4): 277 Size of set of large itemsets L(5): 33 Best rules found: 1. age='(-inf-34.333333]' 195 ==> income='(-inf-24386.173333]' 174 conf:(0.89) lift:(1.88) < lev:(0.14) [81]> conv:(4.65) 2. income='(-inf-24386.173333]' 285 ==> age='(-inf-34.333333]' 174 conf:(0.61) lift:(1.88) < lev:(0.14) [81]> conv:(1.72) 3. age='(-inf-34.333333]' 195 ==> income='(-inf-24386.173333]' current_act=YES 138 conf:(0.71) lift:(1.97) < lev:(0.11) [68]> conv:(2.16) 4. income='(-inf-24386.173333]' current_act=YES 215 ==> age='(-inf-34.333333]' 138 conf:(0.64) lift:(1.97) < lev:(0.11) [68]> conv:(1.86) 5. income='(-inf-24386.173333]' 285 ==> age='(-inf-34.333333]' current_act=YES 138 conf:(0.48) lift:(1.9) < lev:(0.11) [65]> conv:(1.43) 6. age='(-inf-34.333333]' current_act=YES 153 ==> income='(-inf-24386.173333]' 138 conf:(0.9) lift:(1.9) < lev:(0.11) [65]> conv:(5.02) |
| Conviction |
Minimum support: 0.25 (150 instances) Minimum metric <conviction>: 1.1 Number of cycles performed: 15 Generated sets of large itemsets: Size of set of large itemsets L(1): 21 Size of set of large itemsets L(2): 59 Size of set of large itemsets L(3): 16 Best rules found: 1. age='(-inf-34.333333]' 195 ==> income='(-inf-24386.173333]' 174 conf:(0.89) lift:(1.88) lev:(0.14) [81] < conv:(4.65)> 2. mortgage=NO pep=NO 209 ==> married=YES 171 conf:(0.82) lift:(1.24) lev:(0.06) [33] < conv:(1.82)> 3. income='(-inf-24386.173333]' 285 ==> age='(-inf-34.333333]' 174 conf:(0.61) lift:(1.88) lev:(0.14) [81] < conv:(1.72)> 4. age='(50.666667-inf)' 191 ==> save_act=YES 151 conf:(0.79) lift:(1.15) lev:(0.03) [19] < conv:(1.44)> 5. save_act=YES pep=NO 235 ==> married=YES 175 conf:(0.74) lift:(1.13) lev:(0.03) [19] < conv:(1.31)> 6. married=YES mortgage=NO 261 ==> pep=NO 171 conf:(0.66) lift:(1.21) lev:(0.05) [29] < conv:(1.31)> 7. pep=NO 326 ==> married=YES 242 conf:(0.74) lift:(1.12) lev:(0.04) [26] < conv:(1.3)> 8. children=0 263 ==> pep=NO 167 conf:(0.63) lift:(1.17) lev:(0.04) [24] < conv:(1.24)> 9. married=YES save_act=YES 277 ==> pep=NO 175 conf:(0.63) lift:(1.16) lev:(0.04) [24] < conv:(1.23)> 10. current_act=YES pep=NO 244 ==> married=YES 177 conf:(0.73) lift:(1.1) lev:(0.03) [15] < conv:(1.22)> |
至此關聯挖掘的實驗結束。
(七)WEKA實驗改進:二次開發
實驗目的
-
實驗目的
(1)嘗試簡單的WEKA二次開發入門知識;
(2)總結網絡及論壇的各類二次開發經驗與技巧;
(3)實踐二次開發的小實驗。
WEKA二次開發的准備工作
1)WEKA二次開發的准備分析
通過咨詢老師和查閱網絡論壇資料,發現WEKA二次開發過程提供的教程良莠不齊。原因歸結於幾點:
a)WEKA版本眾多,穩定版開發版嘗試的人均不一樣,操作的順序流程也不盡相同。
b)WEKA的二次開發涉及到Eclipse,不同版本的操作軟件集成環境也不盡相同。
c)提供的教程有時不充分,涉及到關鍵的部分時,總是沒有詳細解釋,故需要摸索一條符合實際情況的道路。
2)WEKA二次開發的版本准備
通過結合網絡資料,決定要么采用WEKA3-4-X的穩定版,要么采用WEKA3-6-X穩定版本,其中值得一提的是對於WEKA3-5-X網絡論壇網友也有嘗試,應該是成功的,但是局限在WEKA3-5版本內。二次開發的流程區別在3-4和3-5之間成為分界嶺,但應當是細節的不同。歷屆版本的下載網址見如下:
http://sourceforge.net/projects/weka/files/weka-3-4-windows/
這里我展示WEKA3-4-10和WEKA3-6-11的二次開發環境配置。
3)WEKA3-4-10二次開發的配置
WEKA3-4-10的界面如下:

建立Eclipse的java項目工程,該過程不贅述,然后項目工程的基本目錄結構要有,也就是說,得有src文件夾,鏈接庫等。繼而開始正式的步驟,右鍵點擊weka項目名,點擊導入,並選擇文件系統,所選的文件系統為你weka軟件目錄下的weka-src文件(如果是jar包狀態,請解壓后導入),導入操作如圖所示:
然后選擇導入文件的路徑,在這里請注意!非常重要的是,一定要選擇正確目錄。請與java程序中package的路徑一致,不要跟網絡論壇資料人雲亦雲!網絡論壇教程資料有時不符合你的實際情況。
我通過舉例子,下圖中有帶"勾"的目錄,和帶"叉"的目錄。"勾"是正確的目錄,"叉"是錯誤路徑,而"叉"往往都是教程提供的路徑,這是因為java程序中package的內容是weka.xxxxxx。那么目錄一定要在weka/xxxxx下。

所以導入后,各src下的程序文件都帶有weka的"頭",如下圖細紅框標注所示。如果設置錯誤的路徑,則會提示error,關於無法找到package路徑的問題。通過設置正確的文件目錄后,基本已無error,大多出現warning,如下圖粗紅框內所示,這是正常現象。現在已經可以做開發編程,warning並不會影響。

4)WEKA3-6-11二次開發的配置
WEKA3-6-11導入的也是WEKA3-6-11目錄下的weka-src,如果weka-src是jar包狀態,請解壓后導入。導入的路徑選擇為下圖所示,其中紅色框為路徑節點,藍色框是點擊選擇的目錄路徑,即鼠標點擊紅色框內文件夾名字,再點擊確定即可:

導入后會出現無法解析類型的錯誤,如下圖所示。
一般是缺少動態庫所致,此時需要鏈接相關的庫。鏈接的庫jar包全部在weka-src文件夾lib目錄下。可對Eclipse的JRE系統庫上右鍵點選"構建路徑-配置構建路徑",在彈出的窗口中,選擇"添加外部jar",進入剛才所說的路徑,把三個jar導入。此時Eclipse左側欄產生了"引用庫"內容。

此時不再提示無法解析類型錯誤,但仍然有一些錯誤未能解決,例如"覆蓋超類的錯誤"沒有解決,此問題一般是Java編譯器版本設置問題。

修改你的eclipse指定的編譯器版本。在選項里的java compiler中指定版本至少在5.0以上。在myEclipse中改變編譯器的方法:Project->Properties->JavaCompiler
->ConfigureWorkspace Setting,在彈出的頁面中可以進行設置。
如果是Eclipse下,請在"窗口-首選項"下的彈出窗口內,操作如圖:

然后點選"java-編譯器",更改一致性級別,調整至1.7,此數值對應編譯5.0版本以上,可以提供兼容覆蓋超類語法的編譯環境。



至此,所有錯誤都消失,但Warning存在,仍然是不影響二次開發編程的。通過找到"weka gui"下的"guichooser.java",點擊運行,即可看見wekaGUI界面。如需要添加自己的程序,則添加新的java程序,編寫符合weka接口的新程序,再通過可視化界面運行即可。

##########點擊運行,即可看見wekaGUI界面。如需要添加自己的程序,則添加新的java程序,編寫符合weka接口的新程序,再通過可視化界面運行即可。
WEKA二次開發小實驗
1)WEKA的classpath鏈接開發
咨詢老師和查閱互聯網,教程提供各種不對,所以有一句話說得好,要走出自己的特色道路,不要迷信於別人的道路,不要迷失自己的道路,對自己的道路要自信。這句話意味着,不要認為自己完全不明白,WEKA和java eclipse實驗做到這個份上,有些知識要自己去舉一反三,觸類旁通。以增加LibSvm的jar包為例,這里我們采用鏈接至系統路徑中方法,使WEKA的jar包調用。
靈感在於,咨詢老師時,老師建議使用支持向量機的方法去對垃圾郵件作訓練。那么如果需要該方法訓練,需要下載與WEKA版本對應的jar庫,有兩個,一個是libsvm.jar,另一個是wlsvm.jar。下載地址可以是:
http://www.cs.iastate.edu/~yasser/wlsvm/
下圖展示的是WEKA3-4版本就沒有保留LibSvm的位置,故無此項。

下圖展示的是WEKA3-6版本,它雖然保留LibSvm的位置,但如果缺少其jar包,則顯示為藍色狀態。由於我的WEKA為3-6版本,當時Classify里的Classifier-functions內Libsvm是藍色的,即未連接狀態。即使運行,也會報出錯誤。

所報錯誤如下:

這是由於沒有相關庫,而強行運行所致。即便下載了相關庫,不進行環境變量的添加,也不行。此時不要迷信別人的方法,也不要迷信我的方法,我的方法是我根據自己的情況摸索出來的。
首先要配置WEKA安裝目錄下的文件RunWeka.ini配置文件,打開后,觀察其中一句語句。顯然此語句提供的了classpath的路徑。(網上的修改方法完全忽略此語句,如果不注意到這個細節,則南轅北轍。這也是因為3-6版本較高不同於以前的版本的緣故,所以參考別人教程要注意自己的環境。)

然后根據該配置文件的案例,即
# cp=%CLASSPATH%;D:/libraries/libsvm.jar
我們模仿一下,在上面的這個注釋下方一行,添加一條語句,類似可如下:
cp=%CLASSPATH%;E:/Weka-3-6/wlsvm.jar;E:/Weka-3-6/libsvm.jar;
請注意,上面的指令具備兩個條件,一是你要把這兩個相關庫放在WEKA目錄下,二是指令要寫在注釋行下方。然后保存關閉,點擊RunWeka.bat去更新(記住用管理員權限打開一次,然后再雙擊打開一次。前者方式的打開會打不開,后者的方式會打開。個中玄妙,唯有實踐者才會體會)。此時再打開WEKA-Explorer,尋找Libsvm功能時,是黑色的,可選的,可用的,如下所示:

說明此時可以運行Libsvm成功,至於運行是否成功還未可知。
我們使用Libsvm庫對垃圾郵件做分類,一般來說調用Libsvm會出現兩個錯誤:
| 一是,start后報錯:problem evaluating classifier: rand 該問題解決方法是,更新libsvm較高,原因是libsvm的版本低。更為重要的是,WLSvm的文件包下載下來時是ZIP壓縮文件,打開后里面會有libsvm,記住不要用WLSvm里的libsvm,這個libsvm不是WEKA軟件里鏈接命名的libsvm。需要下載libsvm.zip壓縮包里的文件,並引用這里面的文件。 二是,報錯關於不能處理Could not handle Numeric數據 簡單,把分類關鍵標簽離散化為二值。以郵件過濾為例,class標簽要么是msg代表正常郵件,要么是spam代表垃圾郵件。我們只要離散化class的值即可。我自己操作的時候把msg標為1,spam標為2。 |
其中離散化成為Nominal類型的圖片結果如下:

使用Libsvm生成訓練結果的圖片如下:

文字結果摘要如下:
| === Classifier model (full training set) === LibSVM wrapper, original code by Yasser EL-Manzalawy (= WLSVM) Time taken to build model: 27.18 seconds === Stratified cross-validation === === Summary === Correctly Classified Instances 1938 88.7363 % Incorrectly Classified Instances 246 11.2637 % Kappa statistic 0.3849 Mean absolute error 0.1126 Root mean squared error 0.3356 Relative absolute error 43.1037 % Root relative squared error 92.9061 % Total Number of Instances 2184 === Detailed Accuracy By Class === TP Rate FP Rate Precision Recall F-Measure ROC Area Class 1 0.73 0.882 1 0.938 0.635 msg 0.27 0 1 0.27 0.425 0.635 spam Weighted Avg. 0.887 0.617 0.901 0.887 0.859 0.635 === Confusion Matrix === a b <-- classified as 1847 0 | a = msg 246 91 | b = spam |
觀察相關參數,知支持向量機下的精度要比K近鄰懶惰算法還要好,召回率與K懶惰算法不相上下,對於正例判決是滿分,即百分百,足見支持向量機果然大名鼎鼎。
后記
感謝老師的幫助,沒有兩位老師的課上教學和課下指導,我是根本不可能做出來的。對於WEKA的理解和HADOOP的理解又不斷深入。
本科畢業的時候,我的本科導師就教導過道德經中有雲"為學日益,為道日損,損之又損,以至於無為,無為而無不為。"求學日益精進,實踐日益漸辛。等到一天,求學不斷進步,實踐不斷圓滿,以至於無為,無為也就無不為了,當然這也是理想的境界,也是不斷追求的境界。
謝謝老師!真的是麻煩您了!
<<<<<<<<< 寫在頁面最底的小額打賞 >>>>>>>>>
如果讀者親願意的話,可以小額打賞我,感謝您的打賞。您的打賞是我的動力,非常感激。
必讀:如您願意打賞,打賞方式任選其一,本頁面右側的公告欄有支付寶方式打賞,微信方式打賞。
避免因打賞產生法律問題,兩種打賞方式的任一打賞金額上限均為5元,謝謝您的支持。
如有問題,請24小時內通知本人郵件。
