NLP入门 - 新闻文本分类 Task3

Task3 基于机器学习的文本分类

学习目标

  • 学会TF-IDF的原理和使用
  • 使用sklearn的机器学习模型完成文本分类

文本表示方法 Part1

文本表示成计算机能够运算的数字或向量的方法一般称为词嵌入(Word Embedding)方法:将不定长的文本转换到定长的空间内。

1. One-hot
将每一个单词使用一个离散的向量表示:将每个字/词编码一个索引,然后根据索引进行赋值。
e.g.,
句子1:我 爱 北 京 天 安 门
句子2:我 喜 欢 上 海

  • 首先对所有句子的字进行索引:
    { '我': 1, '爱': 2, '北': 3, '京': 4, '天': 5, '安': 6, '门': 7,
    '喜': 8, '欢': 9, '上': 10, '海': 11}
  • 每个字转换为一个11维度稀疏向量:
    我:[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    爱:[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    ...
    海:[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]

2. Bag of Words / Count Vectors
每个文档的字/词用出现次数来表示。
句子1:我 爱 北 京 天 安 门 -> [1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0]
句子2:我 喜 欢 上 海 -> [1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1]

  • 可以用sklearn中的CountVectorizer实现:
from sklearn.feature_extraction.text import CountVectorizer
corpus = [
    'This is the first document.',
    'This document is the second document.',
    'And this is the third one.',
    'Is this the first document?',
]
vectorizer = CountVectorizer()
X = vectorizer.fit_transform(corpus)
print(X.toarray()) # 词频结果
print(vectorizer.get_feature_names()) # 词袋中所有文本关键词

Result:

[[0 1 1 1 0 0 1 0 1]
 [0 2 0 1 0 1 1 0 1]
 [1 0 0 1 1 0 1 1 1]
 [0 1 1 1 0 0 1 0 1]]
['and', 'document', 'first', 'is', 'one', 'second', 'the', 'third', 'this']

3. N-gram
与Count Vectors类似,不过加入了相邻单词组成新的单词,并进行计数。
e.g., N取值为2,就变为:
句子1:我爱 爱北 北京 京天 天安 安门
句子2:我喜 喜欢 欢上 上海

4. TF-IDF

  • term frequency-inverse document frequency: a statistical measure used to evaluate how important a word is to a document in a collection or corpus.
    TF(t)= 该词语在当前文档出现的次数 / 当前文档中词语的总数
    IDF(t)= log_e(文档总数 / 出现该词语的文档总数)
    tfidf_{i,j} = tf_{i,j} \times idf_{i,j}
  • 可以用sklearn中的TfidfVectorizer实现:
from sklearn.feature_extraction.text import TfidfVectorizer
corpus = [
    'This is the first document.',
    'This document is the second document.',
    'And this is the third one.',
    'Is this the first document?',
]
vectorizer = TfidfVectorizer()
X = vectorizer.fit_transform(corpus)
print(X.toarray())
print(vectorizer.get_feature_names())

Result:

[[0.         0.46979139 0.58028582 0.38408524 0.         0.
 0.38408524 0.         0.38408524]
[0.         0.6876236  0.         0.28108867 0.         0.53864762
 0.28108867 0.         0.28108867]
[0.51184851 0.         0.         0.26710379 0.51184851 0.
 0.26710379 0.51184851 0.26710379]
[0.         0.46979139 0.58028582 0.38408524 0.         0.
 0.38408524 0.         0.38408524]]
['and', 'document', 'first', 'is', 'one', 'second', 'the', 'third', 'this']

基于机器学习的文本分类

对比不同文本表示算法的精度,通过本地构建验证集计算F1得分。

1. Count Vectors + RidgeClassifier

import pandas as pd

from sklearn.feature_extraction.text import CountVectorizer
from sklearn.linear_model import RidgeClassifier
from sklearn.metrics import f1_score

train_df = pd.read_csv('data/train_set.csv', sep='\t', nrows=15000)

vectorizer = CountVectorizer(max_features=3000)
train_test = vectorizer.fit_transform(train_df['text'])

clf = RidgeClassifier()
clf.fit(train_test[:10000], train_df['label'].values[:10000])
val_pred = clf.predict(train_test[10000:])

print("Count Vectors + RidgeClassifier: f1_score =", end=' ')
print(f1_score(train_df['label'].values[10000:], val_pred, average='macro'))
# 0.74

2. TF-IDF + RidgeClassifier

import pandas as pd

from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import RidgeClassifier
from sklearn.metrics import f1_score

train_df = pd.read_csv('data/train_set.csv', sep='\t', nrows=15000)

tfidf = TfidfVectorizer(ngram_range=(1,3), max_features=3000)
train_test = tfidf.fit_transform(train_df['text'])

clf = RidgeClassifier()
clf.fit(train_test[:10000], train_df['label'].values[:10000])
val_pred = clf.predict(train_test[10000:])

print("TF-IDF \t\t  + RidgeClassifier: f1_score =", end=' ')
print(f1_score(train_df['label'].values[10000:], val_pred, average='macro'))
# 0.87

Result:

Count Vectors + RidgeClassifier: f1_score = 0.7406241569237678
TF-IDF        + RidgeClassifier: f1_score = 0.8721598830546126

本章作业

  1. 尝试改变TF-IDF的参数,并验证精度
    tfidf = TfidfVectorizer(ngram_range=(1, 1), max_features=None)
  • 参数含义:
    ngram_range=(min, max) - 将text分成min~max 个不同的词组
    比如'Python is useful'中ngram_range(1,3)可得到'Python' 'is' 'useful' 'Python is' 'is useful' 和'Python is useful';如果是ngram_range (1,1) 则只能得到单个单词'Python' 'is'和'useful'
    max_features: int - build a vocabulary that only consider the top max_features ordered by term frequency across the corpus.
    Set a certain threshold for word frequences. e.g., threshold=50, and data corpus consists of 100 words. After looking at the word frequences 20 words occur less than 50 times. Thus, set max_features=80.

  • 改变max_features

for max_features in range(1000, 10000 , 1000):
    tfidf = TfidfVectorizer(ngram_range=(1,3), max_features=max_features)
    train_test = tfidf.fit_transform(train_df['text'])

    clf = RidgeClassifier()
    clf.fit(train_test[:10000], train_df['label'].values[:10000])

    val_pred = clf.predict(train_test[10000:])
    print("max_features =",max_features,": f1_score =", end=' ')
    print(f1_score(train_df['label'].values[10000:], val_pred, average='macro'))

Result:

max_features = 1000 : f1_score = 0.8270776630718544
max_features = 2000 : f1_score = 0.8603842642428617
max_features = 3000 : f1_score = 0.8721598830546126
max_features = 4000 : f1_score = 0.8753945850878357
max_features = 5000 : f1_score = 0.8850817067811825
max_features = 6000 : f1_score = 0.8901406771892212
max_features = 7000 : f1_score = 0.8920634181410882
max_features = 8000 : f1_score = 0.8897593080180294
max_features = 9000 : f1_score = 0.89142965492415

发现max_features=7000时f1_score最高(0.8921)。

  • 选择max_features=7000,改变ngram_range
for ngram_max in range(1, 5):
    tfidf = TfidfVectorizer(ngram_range=(1,ngram_max), max_features=7000)

Result:

ngram_range=(1,1), f1_score = 0.8603325900148268
ngram_range=(1,2), f1_score = 0.8875087923712194
ngram_range=(1,3), f1_score = 0.8920634181410882
ngram_range=(1,4), f1_score = 0.891603038208734
ngram_range=(1,5), f1_score = 0.8914513820822496
  1. 尝试使用其他机器学习模型,完成训练和验证
  • Logistic Regression classifier (0.8589)
from sklearn.linear_model import LogisticRegression
clf = LogisticRegression(max_iter=10000)
  • Linear Support Vector Classification (0.8964)
from sklearn.svm import LinearSVC
clf = LinearSVC(max_iter=10000)

最优结果:0.8964


Refereces:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,922评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,591评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,546评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,467评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,553评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,580评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,588评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,334评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,780评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,092评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,270评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,925评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,573评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,194评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,437评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,154评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,127评论 2 352