目录
1、介绍
背景
https://tianchi.aliyun.com/competition/entrance/531810/introduction
https://tianchi.aliyun.com/notebook-ai/detail?postId=118252
14个候选分类类别。训练集20w条样本,测试集A包括5w条样本,测试集B包括5w条样本。
2、数据处理
import pandas as pd
train_df = pd.read_csv('../data/train_set.csv', sep='\t', nrows=100) # nrows,为此次读取文件的函数,是数值类型(由于数据集比较大,建议先设置为100)
train_df.head()
句子长度分析
在赛题数据中每行句子的字符使用空格进行隔开,所以可以直接统计单词的个数来得到每个句子的长度。
%pylab inline
train_df['text_len'] = train_df['text'].apply(lambda x: len(x.split(' ')))
print(train_df['text_len'].describe())
_ = plt.hist(train_df['text_len'], bins=200)
plt.xlabel('Text char count')
plt.title("Histogram of char count")
新闻类别分布
对数据集的类别进行分布统计,具体统计每类新闻的样本个数。
train_df['label'].value_counts().plot(kind='bar')
plt.title('News class count')
plt.xlabel("category")
通过数据分析得出以下结论:
- 每个新闻平均字符个数较多,可能需要截断;
- 由于类别不均衡,会严重影响模型的精度;
https://tianchi.aliyun.com/notebook-ai/detail?spm=5176.12586969.1002.9.6406111aIKCSLV&postId=118253
3、Count Vectors、TF-IDF
Bag of Words(词袋表示),也称为Count Vectors,每个文档的字/词可以使用其出现次数来进行表示。
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()
vectorizer.fit_transform(corpus).toarray()
TF-IDF 分数由两部分组成:第一部分是词语频率(Term Frequency),第二部分是逆文档频率(Inverse Document Frequency)。其中计算语料库中文档总数除以含有该词语的文档数量,然后再取对数就是逆文档频率。
TF(t)= 该词语在当前文档出现的次数 / 当前文档中词语的总数
IDF(t)= log_e(文档总数 / 出现该词语的文档总数)
Count Vectors + RidgeClassifier
# 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(f1_score(train_df['label'].values[10000:], val_pred, average='macro'))
# 0.74
TF-IDF + RidgeClassifier
# 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(f1_score(train_df['label'].values[10000:], val_pred, average='macro'))
# 0.87
https://tianchi.aliyun.com/notebook-ai/detail?spm=5176.12586969.1002.12.6406111aIKCSLV&postId=118254
baseline视频 https://tianchi.aliyun.com/course/video?liveId=41190
4、FastText
keras实现的FastText网络结构
import pandas as pd
from sklearn.metrics import f1_score
# 转换为FastText需要的格式
train_df = pd.read_csv('../data/train_set.csv', sep='\t', nrows=15000)
train_df['label_ft'] = '__label__' + train_df['label'].astype(str)
train_df[['text','label_ft']].iloc[:-5000].to_csv('train.csv', index=None, header=None, sep='\t')
import fasttext
model = fasttext.train_supervised('train.csv', lr=1.0, wordNgrams=2,
verbose=2, minCount=1, epoch=25, loss="hs")
val_pred = [model.predict(x)[0][0].split('__')[-1] for x in train_df.iloc[-5000:]['text']]
print(f1_score(train_df['label'].values[-5000:].astype(str), val_pred, average='macro'))
# 0.82
https://tianchi.aliyun.com/notebook-ai/detail?spm=5176.12586969.1002.15.6406111aIKCSLV&postId=118255
5、Word2Vec
数据预处理(略)
logging.info('Start training...')
from gensim.models.word2vec import Word2Vec
num_features = 100 # Word vector dimensionality
num_workers = 8 # Number of threads to run in parallel
# 预处理好的train_texts
train_texts = list(map(lambda x: list(x.split()), train_texts))
model = Word2Vec(train_texts, workers=num_workers, size=num_features)
model.init_sims(replace=True)
# save model
model.save("./word2vec.bin")
# load model
model = Word2Vec.load("./word2vec.bin")
# convert format
model.wv.save_word2vec_format('./word2vec.txt', binary=False)
https://tianchi.aliyun.com/notebook-ai/detail?spm=5176.12586969.1002.18.6406111aIKCSLV&postId=118268
6、TextCNN
https://tianchi.aliyun.com/notebook-ai/detail?postId=118258
7、TextRNN
https://tianchi.aliyun.com/notebook-ai/detail?spm=5176.12281915.0.0.654e3df6jW5TBL&postId=118259
8、BERT
https://tianchi.aliyun.com/notebook-ai/detail?spm=5176.12586969.1002.27.6406111aIKCSLV&postId=118260
BertModel类,它就是BERT模型的基本代码。我们可以看到它的类定义中,由embedding,encoder,pooler组成,forward时顺序经过三个模块,输出output。
- BertEmbeddings这个类中,embedding由三种embedding相加得到,经过layernorm 和 dropout后输出。
- BertEncoder主要将embedding的输出,逐个经过每一层Bertlayer的处理,得到各层hidden_state,再根据config的参数,来决定最后是否所有的hidden_state都要输出。
- Bertpooler 其实就是将BERT的[CLS]的hidden_state 取出,经过一层DNN和Tanh计算后输出。
BERT,主要思想是:采用 Transformer 网络作为模型基本结构,在大规模无监督语料上通过掩蔽语言模型和下句预测两个预训练任务进行预训练(Pre-training),得到预训练模型。
再以预训练模型为基础,在下游相关 NLP 任务上进行模型微调(Fine-tuning)。BERT 预训练模型能够充分利用无监督预训练时学习到的语言先验知识,在微调时将其迁移到下游 NLP 任务上。
BERT 模型的模型结构主要由三部分构成:输入层、编码层和任务相关层。
- 输入层包括词嵌入(token embedding)、位置嵌入(position embedding)段嵌入(segment embedding),并将三者相加得到每个词的输入表示。
- 编码层直接使用了 Transformer 编码器来编码输入序列的表示。
- 任务相关层则根据下游任务不同而有所不同,如对于文本分类任务,任务相关层通常为带 softmax 的线性分类器。
通过这两个预训练任务,BERT 模型能够学习到先验的语言知识,并在后面迁移给下游任务。
- MLM的原理是:随机选取输入序列中的一定比例(15%)的词,用掩蔽标记 [MASK] 替换,然后根据双向上下文的词预测这些被掩蔽的词。
- NSP任务的主要目标是:根据输入的两个句子 A 和 B,预测出句子 B 是否是句子 A 的下一个句子。
经过预训练的 BERT 模型可以用于下游的自然语言处理任务。在使用时,主要是在预训练 BERT 模型的基础上加入任务相关层,再在特定任务上进行微调(fine-tuning)。
通常,我们取出 BERT 模型最后一层的向量表示,送入任务相关层中,就可以得到任务所要建模的目标概率。文本分类取出最后一层 [CLS] 标记对应的向量表示,再进行线性变换和 softmax 归一化就可以得到分类概率。在微调时,BERT 模型和任务相关层的所有参数都一起更新,最优化当前下游任务的损失函数。