阿里云安全算法挑戰(zhàn)賽網(wǎng)頁文本分類總結

第一次參加阿里天池的比賽,雖然總成績沒能進前十,但是網(wǎng)頁題做的還不錯(最終排名第二,成績0.8312分)。在這里記錄一下網(wǎng)頁文本分類題的解題思路和賽后總結:

出題背景是阿里云服務器上存在著很多非法網(wǎng)頁,需要通過機器學習算法識別出網(wǎng)頁的類別,對非法網(wǎng)頁進行打擊。題目的詳細描述見賽題官方網(wǎng)址賽題二。原始數(shù)據(jù)是網(wǎng)頁靜態(tài)html代碼,但可轉換為文本分類問題。訓練集的標簽已給出,共有4個類別:

  1. fake_card(證件卡票):提供偽造證件、發(fā)票、證明材料等制作服務;
  2. gambling(賭博類):包括賭博交易、網(wǎng)絡博彩、賭博器具等;
  3. sexy(色情類):包括色情傳播、文圖視頻、網(wǎng)絡招嫖等;
  4. normal(正常類):不包含以上內(nèi)容的網(wǎng)頁。
網(wǎng)頁分類

算法整體流程圖:

整體流程圖

關鍵步驟:

數(shù)據(jù)預處理

訓練數(shù)據(jù)中,id字段是經(jīng)過hash處理的,會出現(xiàn)id重復的情況,需要根據(jù)id對數(shù)據(jù)進行去重;另外對各類型網(wǎng)頁html文本長度的統(tǒng)計結果如下所示:

網(wǎng)頁文本長度統(tǒng)計圖

網(wǎng)頁文本長度大于200000的不到網(wǎng)頁數(shù)據(jù)總量的1%,且基本不包含風險網(wǎng)頁,所以對其進行清除,減少后續(xù)的計算量;觀察出字符長度小于200的網(wǎng)頁中大部分內(nèi)容為"404 ERROR","正在等待響應...",'"排隊中..."等內(nèi)容,對模型的訓練沒有用處,也要進行清除。代碼在下方:

--計算id的出現(xiàn)次數(shù) 
select id, risk, html, count(id) over (partition by id) as count from ${t1} ;
--計算html文本的長度
select *, length(html) as html_length from ${t1} ;
--數(shù)據(jù)清理
select * from ${t1} where count = 1 and html_length > 200 and html_length < 200000 ;

網(wǎng)頁文本提取

html字段是網(wǎng)頁的源代碼,包含了結構化的html標簽,<style>、<script>等標簽中的內(nèi)容不包含語義信息,參考文獻[5]中結論,實驗中只對title、keywords、description、body標簽中的文本進行提取,另外去除文本中的特殊字符,僅保留英文、中文以及常見的標點符號。html中文本內(nèi)容的提取通過在MapReduce的map函數(shù)中調用jsoup庫實現(xiàn)(因為ODPS有沙箱隔離的限制,實驗中抽取了jsoup的核心代碼放到自定義的包中打包上傳)

package cn.edu.whu.tianchi_htmlclassify;

import cn.edu.whu.tianchi_htmlclassify.nodes.Document;
import cn.edu.whu.tianchi_htmlclassify.nodes.Element;
import cn.edu.whu.tianchi_htmlclassify.select.Elements;

import com.aliyun.odps.OdpsException;
import com.aliyun.odps.data.Record;
import com.aliyun.odps.data.TableInfo;
import com.aliyun.odps.mapred.JobClient;
import com.aliyun.odps.mapred.MapperBase;
import com.aliyun.odps.mapred.conf.JobConf;
import com.aliyun.odps.mapred.utils.InputUtils;
import com.aliyun.odps.mapred.utils.OutputUtils;
import com.aliyun.odps.mapred.utils.SchemaUtils;

import java.io.IOException;

public class HtmlExtractor {
    public static void main(String[] args) throws OdpsException {
        JobConf job = new JobConf();
        job.setMapperClass(MapperClass.class);
        job.setMapOutputKeySchema(SchemaUtils.fromString("id:string"));
        job.setMapOutputValueSchema(SchemaUtils.fromString(
                "title:string,keywords:string,description:string,bodytext:string"));
        job.setNumReduceTasks(0);

        InputUtils.addTable(TableInfo.builder().tableName(args[0]).build(), job);
        OutputUtils.addTable(TableInfo.builder().tableName(args[1]).build(), job);

        JobClient.runJob(job);
    }

    public static class MapperClass extends MapperBase {
        private Record output;

        @Override
        public void setup(TaskContext context) throws IOException {
            output = context.createOutputRecord();
        }

        @Override
        public void map(long key, Record record, TaskContext context)
            throws IOException {
            //id原樣設置
            output.set(0, record.get("id").toString());

            String html = record.get("html").toString();
            String bodyText = new String();
            String title = new String();
            String description = new String();
            String keywords = new String();

            Document doc = null;
            try {
                //使用Jsoup解析html
                doc = Jsoup.parse(html); 
                //去除隱藏的html標簽
                doc.select("*[style*=display:none]").remove();   
                //獲取title文本
                Elements titles = doc.select("title");    
                for (Element element : titles) {
                    title = title + element.text();
                }
                //獲取keywords文本
                Elements metaKey = doc.select("meta[name=keywords]");    
                for (Element element : metaKey) {
                    keywords = keywords + element.attr("content");
                }
                //獲取description文本
                Elements metaDes = doc.select("meta[name=description]");    
                for (Element element : metaDes) {
                    description = description + element.attr("content");
                }
                //獲取body文本
                Elements body = doc.select("body");    
                for (Element element : body) {
                    bodyText = bodyText + element.text();
                }
            } catch (Exception e) {
                bodyText = "";
            } finally {
                //僅保留數(shù)字、字母、中文、常用的標點符號
                output.set(1,title.replaceAll(
                          "[^0-9a-zA-Z\u4e00-\u9fa5.,、,。?“”]+", ""));
                output.set(2,keywords.replaceAll(
                          "[^0-9a-zA-Z\u4e00-\u9fa5.,、,。?“”]+", ""));
                output.set(3,description.replaceAll(
                          "[^0-9a-zA-Z\u4e00-\u9fa5.,、,。?“”]+", ""));
                output.set(4,bodyText.replaceAll(
                          "[^0-9a-zA-Z\u4e00-\u9fa5.,、,。?“”]+", ""));
                context.write(output);
            }
        }
    }
}

文本分詞

在中文文本分類中,常用的特征單元有字、詞、字串(character n-gram)及其組合,中文中單個字的語義質量不是很好,一般不作為特征單元,詞與二字串的效果相似。在天池PAI平臺上,有基于AliWS(Alibaba Word Segmenter的簡稱)的分詞組件,若用戶指定了詞性標注或語義標注相關參數(shù),則會將分詞結果、詞性標注結果和語義標注結果一同輸出。在這里勾選詞性標注,在詞的初步過濾中會用到詞性。

文本分詞參數(shù)

詞初步過濾

  1. 停用詞過濾
    使用 https://github.com/JNU-MINT/TextBayesClassifier 中停用詞;
  2. 詞性過濾(僅保留動詞、名詞)
    參考[7]中實驗結果,僅適用動詞、名詞作為特征詞。

詞頻統(tǒng)計

在對文章進行分詞的基礎上,按行保序輸出對應文章ID列(docId)對應文章的詞,統(tǒng)計指定文章ID列(docId)對應文章內(nèi)容(docContent)的詞頻。
使用詞頻統(tǒng)計組件分別對title、keywords、description、bodytext進行詞頻統(tǒng)計,得到文檔i中詞j出現(xiàn)在title、keywords、description、bodytext中的個數(shù)

詞頻統(tǒng)計結果表

可以對title、keywords、description、body設置不同的權重,測試了不同權重的組合,當四個標簽權重都為1時分類精度最高,詞頻權重組合的方法如下所示:

select *, (title_count * 1 + key_count * 1 + des_count * 1 + body_count) as count from ${t1}

特征詞選擇

特征詞的選擇在很多博客與論文[1][6]中都有敘述,常見的特征提取方法有文檔詞頻(Document Frequency, DF)、信息增益(Information Gain, IG)、互信息(Mutual Information, MI)、卡方統(tǒng)計(CHI)、期望交叉熵(Expected Cross Entropy)等等, 由于時間限制,我們試驗了幾種通常情況表現(xiàn)好的方法進行結合:文檔詞頻 + 信息增益 + 卡方增益,對比了幾組不同的閾值,得到了較好的分類效果。

  • 文檔詞頻:在訓練文本集中對每個詞計算其文檔頻數(shù),若該詞的DF值小于某個閾值或者大于某個閾值都要進行去除,因為小于某個閾值代表了該詞“沒有代表性”,大于某個閾值代表了“沒有區(qū)分度”。
  • 信息增益:信息增益是一種基于熵的評估方法,定義為某個詞為分類能夠提供的信息量, 其值為不考慮任何詞的熵與考慮該詞后的熵的差值。計算公式如下:
信息增益計算公式
  • 卡方統(tǒng)計:卡方統(tǒng)計體現(xiàn)特征詞與類別之間獨立性的缺乏程度,它同時考慮了特征詞存在與不存在的情況??ǚ浇y(tǒng)計值越大,獨立性越小,相關性越大。計算公式如下:
卡方統(tǒng)計計算公式
  • 計算步驟:
  1. 統(tǒng)計訓練樣本的文檔的總數(shù)記為total_count;
  2. 統(tǒng)計每個類別的文檔總數(shù)分別為sexy_total_count、gamble_total_count、fake_total_count、normal_total_count;
  3. 計算每個詞在各類別文檔中出現(xiàn)的總次數(shù)分別記為sexy_count、gamble_count、fake_count、normal_count;
  4. 每個詞的文檔頻數(shù)doc_count = sexy_count + gamble_count + fake_count + normal_count;
  5. 根據(jù)下圖中對各類型ABCD值的定義,求出sexyA、sexyB、sexyC、sexyD、gambleA、gambleB、gambleC、gambleD、fakeA、fakeB、fakeC、fakeD、normalA、normalB、normalC、normalD;
ABCD定義
--計算特征詞在各類型中的ABCD值
select *,
sexy_count as sexyA,
doc_count - sexy_count as sexyB,
sexy_total_count - sexy_count as sexyC,
total_count - doc_count - sexy_total_count + sexy_count as sexyD,

gamble_count as gambleA,
doc_count - gamble_count as gambleB,
gamble_total_count - gamble_count as gambleC,
total_count - doc_count - gamble_total_count + gamble_count as gambleD,

fake_count as fakeA,
doc_count - fake_count as fakeB,
fake_total_count - fake_count as fakeC,
total_count - doc_count - fake_total_count + fake_count as fakeD,

normal_count as normalA,
doc_count - normal_count as normalB,
normal_total_count - normal_count as normalC,
total_count - doc_count - normal_total_count + normal_count as normalD

from ${t1}
  1. 根據(jù)卡方統(tǒng)計計算出各類型CHI值,再用下式計算詞t對于整個樣本的CHI值;
卡方統(tǒng)計值
--計算特征詞在各類型中的卡方統(tǒng)計
select *,
total_count * (sexyA * sexyD - sexyC * sexyB) * (sexyA * sexyD - sexyC * sexyB) / 
((sexyA + sexyC) * (sexyB + sexyD) * (sexyA + sexyB) * (sexyC + sexyD)) 
as sexy_x2,

total_count * (gambleA * gambleD - gambleC * gambleB) * (gambleA * gambleD - gambleC * gambleB) / 
((gambleA + gambleC) * (gambleB + gambleD) * (gambleA + gambleB) * (gambleC + gambleD)) 
as gamble_x2,

total_count * (fakeA * fakeD - fakeC * fakeB) * (fakeA * fakeD - fakeC * fakeB) / 
((fakeA + fakeC) * (fakeB + fakeD) * (fakeA + fakeB) * (fakeC + fakeD)) 
as fake_x2,

total_count * (normalA * normalD - normalC * normalB) * (normalA * normalD - normalC * normalB) / 
((normalA + normalC) * (normalB + normalD) * (normalA + normalB) * (normalC + normalD)) 
as normal_x2

from ${t1};

select *, GREATEST(sexy_x2,  gamble_x2, fake_x2, normal_x2) as x2 from ${t1};
  1. 代入信息增益公式計算出word_ig值;

p(csexy) = sexy_total_count / total_count
p(t) = doc_count / total_count
p(csexy| t) = sexy_count / doc_count
p(t) = 1 - doc_count / total_count
p(csexy| t) = (sexy_total_count - sexy_count) / (total_count- doc_count)

select *,
- ((sexy_total_count / total_count * log(2, sexy_total_count / total_count )
 +  gamble_total_count / total_count * log(2, gamble_total_count / total_count )
 +  fake_total_count / total_count * log(2, fake_total_count / total_count ) 
 +  normal_total_count / total_count * log(2, normal_total_count / total_count ) ) 
+ doc_count / total_count * 
  (sexy_count / doc_count * log(2, sexy_count / doc_count + 0.01) + 
   gamble_count / doc_count * log(2, gamble_count / doc_count  + 0.01) + 
   fake_count / doc_count * log(2, fake_count / doc_count  + 0.01) + 
   normal_count / doc_count * log(2, normal_count / doc_count  + 0.01)) 
+ (1 - doc_count / total_count ) * 
  ((sexy_total_count- sexy_count) / (total_count- doc_count) * 
      log(2, (sexy_total_count - sexy_count) / (total_count- doc_count) + 0.01) + 
   (gamble_total_count - gamble_count) / (total_count - doc_count) * 
      log(2, (gamble_total_count - gamble_count) / (total_count- doc_count) + 0.01) + 
   (fake_total_count - fake_count) / (total_count - doc_count) * 
      log(2, (fake_total_count  - fake_count) / (total_count - doc_count) + 0.01) + 
   (normal_total_count- normal_count) / (total_count - doc_count) * 
      log(2, (normal_total_count - normal_count) / (total_count - doc_count) + 0.01))  
as word_ig from ${t1}
  1. 結合doc_count,信息增益,卡方統(tǒng)計選擇特征詞(設置各自的閾值)。

特征計算

特征計算常用tfidf,它的形式為:

tfidf計算公式

其中tf(ti, dj)是詞在當前文本中的局部因子,idf(ti)是只與ti相關的全局因子,在論文[2]中認為idf不能充分反映詞的重要程度,總結了一些改進的方法:
其他特征值計算方法

我們實驗了幾種在論文結論中表現(xiàn)較好的幾種方法:TFIDF、Probability based、tf * info gain,感到驚訝的是傳統(tǒng)的TFIDF的特征計算方法表現(xiàn)最好(當然實驗次數(shù)有限,不能充分證明)。tfidf的計算方法如下:

-- total_count 為總文檔數(shù)
-- doc_count 為特征詞在文檔中出現(xiàn)總次數(shù)
-- count 為在文檔j中特征詞i出現(xiàn)的頻數(shù)
select *, sum(tfidf_square) over ( partition by id) as tfidf_square_sum from 
(select *, count * log(2, total_count / doc_count + 0.01) * count * log(2, total_count / doc_count + 0.01) 
as tfidf_square from ${t1} ) c

select *, count * log(2, total_count / doc_count + 0.01) / sqrt(tfidf_square_sum) as tfidf from ${t1}

模型訓練及預測

在訓練樣本中,各類型的比值大約為50:5:3:3,需要考慮類別不平衡對模型訓練的影響[4]。常見的解決方法有隨機采樣、SMOTE算法、代價矩陣等。我們采用的是隨機采樣算法,將各類型的比值降采樣到10:5:3:3時分類效果最好。

總結

比賽之前對我們對特征工程、類別不平衡問題、特征穿越這些基本問題一無所知,都是在做題的過程中遇到了問題再去查找解決方法,加上比賽經(jīng)驗不足,過程上可謂是磕磕絆絆。給自己總結幾個經(jīng)驗和教訓:

  1. 經(jīng)典的算法適合入手,學習成本低,通過完善往往能夠得到不錯的結果;
  2. 比賽的前期要不斷的嘗試不同的思路,關注賽題官方論壇、查找相關文獻、和隊友交流思路、動手實驗要同步進行;
  3. 比賽后期要考慮試錯的成本,特別是最后關鍵時刻還是要采用最穩(wěn)妥的方法提高成績;
  4. 選擇好的隊友很關鍵,要找和自己有同樣的激情和目標的人,在這里感謝我的隊友們,一起努力走到最后。

比賽中和隊友一起進步了很多,對學習機器學習的熱情也增加了不少,感謝阿里天池給我們提供了這么好的學習平臺。

參考文獻

  1. Zheng Z, Wu X, Srihari R. Feature selection for text categorization on imbalanced data[J]. Acm Sigkdd Explorations Newsletter, 2004, 6(1):80-89.
  2. Liu Y, Han T L, Sun A. Imbalanced text classification: A term weighting approach[J]. Expert Systems with Applications, 2009, 36(1):690-701.
  3. Shen D, Chen Z, Yang Q, et al. Web-page classification through summarization[C]// International ACM SIGIR Conference on Research and Development in Information Retrieval. ACM, 2004:242-249.
  4. He H, Garcia E A. Learning from Imbalanced Data[J]. IEEE Transactions on Knowledge & Data Engineering, 2009, 21(9):1263-1284.
  5. Golub K, Ard? A. Importance of HTML structural elements and metadata in automated subject classification[C]// European Conference on Research and Advanced Technology for Digital Libraries. Springer-Verlag, 2005:368-378.
  6. 王小青. 中文文本分類特征選擇方法研究[D]. 西南大學, 2010.
  7. 段軍峰, 黃維通, 陸玉昌. 中文網(wǎng)頁分類研究與系統(tǒng)實現(xiàn)[J]. 計算機科學, 2007, 34(6):210-213
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容