资源 中文文本分类实践(Text Classification)
Text Classification 文本分类(Text Classification)是自然语言处理中的一个重要应用技术,根据文档的内容或主题,自动识别文档所属的预先定义的类别标签。文本分类是很多应用场景的基础,比如垃圾邮件识别、舆情分析、情感识别、新闻自动分类、智能客服机器人的知识库分类等等。本文分为两个部分: Part 1: 基于scikit-learn机器学习Python库,对比几个传统机器学习方法的文本分类。 Part 2: 基于预训练词向量模型,使用Keras工具进行文本分类。 Text Classification Part 1: 基于scikit-learn机器学习的文本分类方法 语料预处理 生成训练集和测试集 生成数据集 文本特征提取:TF-IDF 构建分类器 Benchmark: 朴素贝叶斯分类器 对新文本应用分类 分类器的评估 构建Logistic Regression分类器 构建SVM分类器 Part 2: 基于预训练模型的CNN文本分类方法-Keras 读取语料 加载预训练词向量模型 使用Keras对语料进行处理 定义词嵌入矩阵 Embedding Layer 构建模型 参考资料 Part 1: 基于scikit-learn机器学习的文本分类方法 基于scikit-learn机器学习的中文文本分类主要分为以下步骤: 语料预处理 生成训练集和测试集 文本特征提取:TF-IDF 构建分类器 分类器的评估 1.语料预处理 定义搜狗新闻文本标签的名称,类似C000008这样的标签是语料的子目录,在网上搜到标签对应的新闻类别,为了便于理解,定义了这个映射词典,并保留原有编号信息。在网上搜索下载搜狗分类新闻.20061127.zip语料并解压至CN_Corpus目录下,解压之后目录结构为: 下面进行语料的切分,将每个类别的前80%作为训练语料,后20%作为测试语料。切分完之后的语料目录如下: 2. 生成训练集和测试集 生成数据集 从上面切分好的语料目录中读取文本并进行分词预处理,输出:训练语料数据(X_train_data)、训练语料标签(y_train)、测试语料数据(X_test_data)、测试语料标签(y_test)。 X_train_data, y_train, X_test_data, y_test = load_datasets() 数据集的形式如下: X_train_data[1000] y_train[1000] '_08_Finance' 3. 文本特征提取:TF-IDF 这个步骤将文档信息,也即每篇新闻被分好词之后的词集合,转为基于词频-文档词频(TF-IDF)的向量,向量的每个元素都是对应于某个词在这个文档中的TF-IDF值,在不同文档中,同一词的TF-IDF是不一样的。所有文档的TF-IDF向量堆放在一起就组成了一个TF-IDF矩阵。注意到这里应该包含了除停用词之外的所有词的TF-IDF值,词的个数构成了向量的维度。 用TfidfVectorizer将文档集合转为TF-IDF矩阵。注意到前面将文本做了分词并用空格隔开。如果是英文,本身就是空格隔开的,而英文的分词(Tokenizing)是包含在特征提取器中的,不需要分词这一步骤。下面在得到了分类器之后,使用新文本进行分类预测时,也是需要先做一下中文分词的。 X_train_tfidf.shape (13500, 223094) len(words) 223094 4. 构建分类器 Benchmark: 朴素贝叶斯分类器 得到了训练样本的文本特征,现在可以训练出一个分类器,以用来对新的新闻文本进行分类。scikit-learn中提供了多种分类器,其中朴素贝叶斯是一个很好的基准,有多个版本的朴素贝叶斯分类器,其中MultinomialNB比较适合于文本分类。 对新文本应用分类 对新的文本需要进行分类,那么只需将上面的tfidf_vectorizer应用在新的文本上,调用transform方法而不是fit_transform,将新的文本转换为TF-IDF特征,然后再调用分类器的predict,得到分类。 下面新闻节选自腾讯新闻网: 周鸿祎金融梦,营收20亿元直逼趣店,净利润同比增340% 录取率低过5%的美国名校,为何“花钱”就能上? 特朗普:伊朗对美军动武将会“灭亡” 5. 分类器的评估 有了分类器,以及知道了如何用分类器来对新的文本进行分类预测,那么可以用前面划分出来的测试集对这个分类器进行性能评估。得到了84.35%的准确率,作为Benchmark,这个结果还不错。调用classification_report可以得到更详细的结果: print(classification_report(y_test,predicted)) 再看一下混淆矩阵: confusion_matrix(y_test, predicted) 构建Logistic Regression分类器 再试一下其他的分类器,比如Logistic Regression,训练新的分类器: 最后测试结果还行,比Benchmark分类器好了不少。 构建SVM分类器 在传统机器学习中,SVM是做文本分类最好的工具。同样做一下跟上面LR一样的对比: 准确率不低: 传统机器学习方法这样的结果非常不错了,而且实现方式非常简单实用。 Part 2: 基于预训练模型的CNN文本分类方法-Keras 文本分类主要分为以下步骤: 读取语料 加载中文预训练词向量模型 使用Keras进行数据预处理,生成训练集和测试集 定义词嵌入矩阵 构建模型 1. 读取语料 这里读取原始语料,划分训练集和测试集,放在了后面预处理部分。 texts, labels = load_raw_datasets() 下表是转换后的标签表示: 序号 标签 名称 分类编码 0 C000008 Finance [1, 0, 0, 0, 0, 0, 0, 0, 0] 1 C000010 IT [0, 1, 0, 0, 0, 0, 0, 0, 0] 2 C000013 Health [0, 0, 1, 0, 0, 0, 0, 0, 0] 3 C000014 Sports [0, 0, 0, 1, 0, 0, 0, 0, 0] 4 C000016 Travel [0, 0, 0, 0, 1, 0, 0, 0, 0] 5 C000020 Education [0, 0, 0, 0, 0, 1, 0, 0, 0] 6 C000022 Recruit [0, 0, 0, 0, 0, 0, 1, 0, 0] 7 C000023 Culture [0, 0, 0, 0, 0, 0, 0, 1, 0] 8 C000024 Military [0, 0, 0, 0, 0, 0, 0, 0, 1] 2. 加载预训练词向量模型 解压之后的中文预训练词向量模型的文件格式是文本文件,首行只有两个空格隔开的数字:词的个数和词向量的维度,从第二行开始格式为:词 数字1 数字2 …… 数字300,形式如下: 364180 300 embeddings_index = load_pre_trained() Found 364180 word vectors, dimension 300 3. 使用Keras对语料进行处理 代码中word_index表示发现的所有词,得到的文本序列取的是word_index中前面20000个词对应的索引,文本序列集合中的所有词的索引号都在20000之前: 我们可以通过生成的词索引序列和对应的索引词典查看原始文本和对应的标签: category_labels[dict_swaped(labels_index)[argmax(labels_categorical[0])]] '_20_Education' 4. 定义词嵌入矩阵 下面创建一个词嵌入矩阵,用来作为上述文本集合词典(只取序号在前MAX_WORDS_NUM的词,对应了比较常见的词)的词嵌入矩阵,矩阵维度(MAX_WORDS_NUM, EMBEDDING_DIM)。矩阵的每一行i代表词典word_index中第i个词的词向量。这个词嵌入矩阵是预训练词向量的一个子集。我们的新闻语料中很可能有的词不在预训练词向量中,这样的词在这个词向量矩阵中对应的向量元素都设为零。还记得上面用pad_sequence补充的0元素么,它对应在词嵌入矩阵的向量也都是零。在本例中,20000个词有92.35%在预训练词向量中。 Embedding Layer 嵌入层的输入数据sequence向量的整数是文本中词的编码,前面看到这个获取序列编码的步骤使用了Keras的Tokenizer API来实现,如果不使用预训练词向量模型,嵌入层是用随机权重进行初始化,在训练中将学习到训练集中的所有词的权重,也就是词向量。在定义Embedding层,需要至少3个输入数据: input_dim:文本词典的大小,本例中就是MAX_WORDS_NUM + 1; output_dim:词嵌入空间的维度,就是词向量的长度,本例中对应EMBEDDING_DIM; input_length:这是输入序列的长度,本例中对应MAX_SEQUENCE_LEN。 本文中还多了两个输入参数weights=[embedding_matrix]和trainable=False,前者设置该层的嵌入矩阵为上面定义好的词嵌入矩阵,即不适用随机初始化的权重,后者设置为本层参数不可训练,即不会随着后面模型的训练而更改。这里涉及了Embedding层的几种使用方式: 从头开始训练出一个词向量,保存之后可以用在其他的训练任务中; 嵌入层作为深度学习的第一个隐藏层,本身就是深度学习模型训练的一部分; 加载预训练词向量模型,这是一种迁移学习,本文就是这样的示例。 5. 构建模型 Keras支持两种类型的模型结构: Sequential类,顺序模型,这个仅用于层的线性堆叠,最常见的网络架构 Functional API,函数式API,用于层组成的有向无环图,可以构建任意形式的架构 为了有个对比,先不加载预训练模型,让模型自己训练词权重向量。Flatten层用来将输入“压平”,即把多维的输入一维化,这是嵌入层的输出转入全连接层(Dense)的必需的过渡。 每个Keras层都提供了获取或设置本层权重参数的方法: layer.get_weights():返回层的权重(numpy array) layer.set_weights(weights):从numpy array中将权重加载到该层中,要求numpy array的形状与layer.get_weights()的形状相同 get_weights方法得到的就是词嵌入矩阵,如果本例中取的词典足够大,这样的词嵌入矩阵就可以保存下来,作为其他任务的预训练模型使用。通过get_config()可以获取每一层的配置信息: model1.layers[0].get_config() 可以将模型训练的结果打印出来 plot_history(history1) 第一个模型训练时间花了大约30分钟训练完30个epoch,这是因为模型需要训练嵌入层的参数,下面第二个模型在第一个模型基础上加载词嵌入矩阵,并将词嵌入矩阵设为不可训练,看是否可以提高训练的效率。 从第二个模型训练结果可以看到预训练模型的加载可以大幅提高模型训练的效率,模型的验证准确度也提升的比较快,但是同时发现在训练集上出现了过拟合的情况。 第三个模型的结构: plot_history(history3) 通过加入池化层MaxPooling1D,降低了过拟合的情况。验证集上的准备度超过了前两个模型,也超过了传统机器学习方法。 转载自https://github.com/lijqhs/text-classification-cn