最近在学习NLP的相关知识,想通过实践来加深对所学知识的认知。因此,自己从网上找了一个中文语料库进行尝试。本文的实践内容包括文件的读取、中文分词、词向量表达、模型构建和模型融合。
本文所采用的语料库为复旦中文文本分类语料库,包含20个类别。但是,这个网盘里面只有train一个文件夹,我就自己人工把它分成了训练集和测试集。
网盘链接:https://pan.baidu.com/s/1833mT2rhL6gBMlM0KnmyKg
密码:zyxa
注:本文重点关注于代码的实践内容,没有背景知识和理论的介绍讲解。新手入门轻喷,欢迎各位大佬指出错误和不足!
首先,我们先完成读取一篇文本文档,去除stopwords,只保留中文字符后进行分词。函数extract_words_one_file()
可以对单个文件进行处理,提取所有中文分词并返回值。
import re
import os
import numpy as np
import pandas as pd
import jieba
import jieba.analyse
#从一个文本文件中提取中文分词,以句子或文章的形式保存在列表里
def extract_words_one_file(filepath,all=True):
#all指的是是否以全文的形式保存所有分词。
#True则表示一个文章的所有分词都储存在一个列表里,
#False则表示每个句子的分词分别存在一个列表里,再以文章的形式储存列表
#打开文本文件函数声明
def open_file(file_text):
with open(file_text, "r",errors='ignore') as fp:
content = fp.readlines()
return content
#只保留中文字符
def remove(text):
remove_chars = r'[^\u4e00-\u9fa5]'
return re.sub(remove_chars, '', text)
#打开stopwords文件函数声明
def open_stop(file_stop):
stopwords = [line.strip() for line in open(file_stop, 'r', encoding='utf-8').readlines()]
return stopwords
#利用jieba进行分词
def seg_sentence(sentence):
sentence_seged = jieba.cut(sentence.strip())
stopwords = open_stop("stopwords.txt")
outstr = ''
for word in sentence_seged:
if word not in stopwords:
if word != '\t':
outstr += word
outstr += " "
return outstr.strip()
inputs = open_file(filepath) #打开要加载的文件
#获取每一句中的分词
words_in_sentence = []
for line in inputs:
line_delete = remove(line)
line_seg = seg_sentence(line_delete) # 这里的返回值是字符串
words_in_sentence.append(line_seg)
words_in_sentence = [x for x in words_in_sentence if x != '']
#利用空格切割获取所有分词
alltokens = []
chinesewords_sentence = []
for i in range(len(words_in_sentence)):
word = re.split(r'\s',words_in_sentence[i])
alltokens.append(word)
#对每一个句子产生的分词所储存的列表,删除空值
for element in alltokens:
element = [x for x in element if x != '']
chinesewords_sentence.append(element)
#获取所有分词的列表
chinesewords_article = [i for k in chinesewords_sentence for i in k]
if all == True:
return chinesewords_article
else:
return chinesewords_sentence
单个文档实现功能之后,我们想要遍历文件夹下的所有文本文件并进行处理。extract_words_folder()
可以实现此功能,并以制定格式和要求保存数据。
#对一个文件夹下的多个文件夹中的文件进行批量处理,获取对应的中文分词以句子或全文形式保存
def extract_words_folder(all = True):
#all指的是是否以全文的形式保存所有分词。
#True则表示所有的分词储存在一个列表里,
#False则表示每个句子的分词分别存在一个列表里,再以文章的形式储存列表
path = "C:.../train" #文件夹目录
files= os.listdir(path) #得到文件夹下的所有文件名称
features = []
for i in range(len(files)):#遍历目录下的所有文件夹
dirs=os.listdir(path + '/' + files[i])
for f in dirs:#遍历该子文件下的所有文件并按照句子或全文形式抽取分词
if all == True:
word_single_text = extract_words_one_file(path+"/"+files[i]+"/"+f,all = True)
word_with_label = [word_single_text,files[i],f] #将所属种类和文件名一并保存
features.append(word_with_label)
else:
word_single_text = extract_words_one_file(path+"/"+files[i]+"/"+f,all = False)
features.append(word_single_text)
if all == True:
return pd.DataFrame(features,columns=['Words','Category','File']) #将分词、种类和文件名以dataframe储存
else:
return features
article_features = extract_words_folder(all = True)
#以csv形式保存dataframe
article_features.to_csv("article_features_train_raw.csv",encoding='utf_8_sig',index=False)
#以文本格式保存句子分词
sent_features = extract_words_folder(all = False)
with open("word_sentence.txt","w") as f:
f.write(str(sent_features))
至此,中文分词工作已经完成。在后续过程中需要使用的分词格式也被分别保存到了csv和txt文件中。
为了方便之后数据的一致性,我没有将所有的数据都一并保存,之后再划分训练集测试集,而是之前手动划分,自己创建了一个test的文件夹,分别提取训练集和测试集的数据,然后分别存入了两个csv文件当中。我在划分的时候也发现,不同类别的样本数量差别很大,有的上千,有的还不过百。因此,我只选取了其中最多的九个类别,去除了剩余的11个类别。因为这些样本数量过少的类别相比于其他悬殊的样本数量,很难让模型作出无偏见的判定。
import pandas as pd
import numpy as np
#raw data预处理,保存用于学习的数据到对应文件
train = pd.read_csv('article_features_train_raw.csv')
test = pd.read_csv('article_features_test_raw.csv')
#部分种类因为样本数太少,无法训练出结果,故删去,只保留九个数量较多的类别
#train文件夹中的Enviornment拼写错误,为了和test统一替换成正确的拼写
train.Category.replace('C31-Enviornment','C31-Environment',inplace=True)
train = train[(train['Category'] == 'C3-Art')|(train['Category'] == 'C11-Space')|(train['Category'] == 'C19-Computer')
|(train['Category'] == 'C31-Environment')|(train['Category'] == 'C32-Agriculture')
|(train['Category'] == 'C34-Economy')|(train['Category'] == 'C38-Politics')|(train['Category'] == 'C39-Sports')
|(train['Category'] == 'C7-History')]
test = test[(test['Category'] == 'C3-Art')|(test['Category'] == 'C11-Space')|(test['Category'] == 'C19-Computer')
|(test['Category'] =='C31-Environment')|(test['Category'] == 'C32-Agriculture')
|(test['Category'] == 'C34-Economy')|(test['Category'] == 'C38-Politics')|(test['Category'] == 'C39-Sports')
|(test['Category'] == 'C7-History')]
#定义标签替换字典
label2category = {
0: 'C11-Space', 1: 'C19-Computer', 2: 'C3-Art', 3: 'C31-Environment', 4: 'C32-Agriculture',
5: 'C34-Economy', 6:'C38-Politics',7:'C39-Sports',8:'C7-History'}
category2label = dict(zip(label2category.values(), label2category.keys()))
train['label'] = train.Category.replace(category2label)
test['label'] = test.Category.replace(category2label)
train = train.reset_index(drop=True)
test = test.reset_index(drop=True)
#保存新数据
train.to_csv("article_features_train.csv",encoding='utf_8_sig',index=False)
test.to_csv("article_features_test.csv",encoding='utf_8_sig',index=False)
分词提取完毕后,我们需要用一定的方式表示这些分词,将这些计算机无法处理的非结构化信息转化为可计算的结构化信息。one-hot方法是其中之一,它的原理很好理解,首先生成一个初始值为零的长度为所有词的列表。若文章或句子包含某词,则该词对应位置为1,否则为0。但是其在表示大规模文本的时候往往出现过于稀疏的现象,也无法表示词语与词语间的关系,所以只是在比较简单的实践中使用。
本文我们所采取的文本表示方法是词嵌入中的Word2Vec。通俗的来说,就是根据训练将分词用多维向量表示。其2种训练模式为通过上下文来预测当前词和通过当前词来预测上下文。
import gensim
#导入数据
with open("word_sentence.txt", "r") as f: #打开文件
word_sentence = f.read() #读取文件
sent_feature = eval(word_sentence)
#我们只选取分词数大于3的句子进行W2v模型训练
sent_words = [i for k in sent_feature for i in k if len(i)>3]
在2.1部分中,我们已经将分词按句子形式保存到了文本文件中,现在,我们便利用它直接进行训练获得模型。
model = gensim.models.Word2Vec(sent_words, sg=1, size=100, window=3,iter=5,
min_count=3, negative=3, sample=0.001, hs=1)
#模型保存
model.wv.save_word2vec_format('./word2vec_model.txt', binary=False)
我在GitHub上看到了有已经训练好的中文词向量模型,个人认为这是一个比较好的资源,毕竟感觉这些模型应该是依赖于很大的文本基数,充分利用可能也会提高最后的准确率。
https://github.com/Embedding/Chinese-Word-Vectors
import gensim
import numpy as np
#导入数据
# 首先初始化一个word2vec 模型:
w2v_model = gensim.models.Word2Vec(size=300, window = 3,sg=1, min_count=3)
w2v_model.build_vocab(sent_words)
# 再加载第三方预训练模型:
third_model = gensim.models.KeyedVectors.load_word2vec_format('sgns.merge.word', binary=False)
# 通过 intersect_word2vec_format()方法merge词向量:
w2v_model.build_vocab([list(third_model.vocab.keys())], update=True)
w2v_model.intersect_word2vec_format('sgns.merge.word', binary=False, lockf=1.0)
w2v_model.train(sent_words, total_examples=w2v_model.corpus_count, epochs=5)
print("Model training finished.")
w2v_model.wv.save_word2vec_format('./word2vec_ensemble.txt', binary=False)
print("Model saved.")
至此,我们已经通过两种方法获得了维度分别为100和300的两个词向量模型。具体使用哪一个大家可以随心,也可以多多比较一下两个词向量模型的表现,来观察哪一个准确率更高。
本文主要介绍了我在读取文本内容,进行中文分词和训练词向量模型的实践内容。下一节,我将主要介绍在本节所获得的词向量的基础上,搭建不同的机器模型学习继续实践的实例。感谢支持,希望多多关注!
注:转载请注明出处!