和之前介绍的基于TF-IDF、CNN、RNN的分类不同,重构了些代码,为了使整个流程更加清楚,我们要重新对数据进行预处理。阅读本文,你可以了解中文文本分类从数据预处理、模型定义、训练和测试的整个流程。
之前的在博客园上:
利用TfidfVectorizer进行中文文本分类(数据集是复旦中文语料):https://www.cnblogs.com/xiximayou/p/13782440.html
利用RNN进行中文文本分类(数据集是复旦中文语料):https://www.cnblogs.com/xiximayou/p/13828988.html
利用CNN进行中文文本分类(数据集是复旦中文语料):https://www.cnblogs.com/xiximayou/p/13842312.html
熟悉数据
数据的格式是这样子的:
基本目录如下:
其中train存放的是训练集,answer存放的是测试集,具体看下train中的文件:
下面有20个文件夹,对应着20个类,我们继续看下其中的文件,以C3-Art为例:
每一篇都对应着一个txt文件,编码格式是gb18030.utf8文件夹下的是utf-8编码格式的txt文件。
其中C3-Art0001.txt的部分内容如下:
数据预处理
本文数据预处理基本流程:
先将所有训练数据的txt路径以及测试用的txt路径写入到txt中备用:train.txt、test.txt
def _txtpath_to_txt(self): #将训练集和测试集下的txt路径保存
train_txt_path = os.path.join(PATH, "process/Fudan/train.txt")
test_txt_path = os.path.join(PATH, "process/Fudan//test.txt")
train_list = os.listdir(os.path.join(PATH, self.trainPath)) #获得该目录下的所有文件夹,返回一个列表
fp1 = open(train_txt_path,"w",encoding="utf-8")
fp2 = open(test_txt_path,"w",encoding="utf-8")
for train_dir in train_list: #取得下一级目录下的所有的txt路径(绝对路径)
for txt in glob.glob(os.path.join(PATH,self.trainPath+train_dir+"/*.txt")):
fp1.write(txt+"\n")
fp1.close()
test_list = os.listdir(os.path.join(PATH,self.testPath)) #获得该目录下的所有文件夹,返回一个列表
for test_dir in test_list:
for txt in glob.glob(os.path.join(PATH, self.testPath+test_dir+"/*.txt")):
fp2.write(txt+"\n")
fp2.close()
接下来我们要将txt中的文本分词后写入到分词文本txt中,以及对应的标签写入到标签txt中:train_content.txt、train_label.txt、test_content.txt、test_label.txt
#将txt中的文本和标签存储到txt中
def _contentlabel_to_txt(self, txt_path, content_path, label_path):
files = open(txt_path,"r",encoding="utf-8")
content_file = open(content_path,"w",encoding="utf-8")
label_file = open(label_path,"w",encoding="utf-8")
for txt in files.readlines(): #读取每一行的txt
txt = txt.strip() #去除掉\n
content_list=[]
label_str = txt.split("/")[-1].split("-")[-1] #先用/进行切割,获取列表中的最后一个,再利用-进行切割,获取最后一个
label_list = []
#以下for循环用于获取标签,遍历每个字符,如果遇到了数字,就终止
for s in label_str:
if s.isalpha():
label_list.append(s)
elif s.isalnum():
break
else:
print("出错了")
label = "".join(label_list) #将字符列表转换为字符串,得到标签
#print(label)
#以下用于获取所有文本
fp1 = open(txt,"r",encoding="gb18030",errors='ignore') #以gb18030的格式打开文件,errors='ignore'用于忽略掉超过该字符编码范围的字符
for line in fp1.readlines(): #读取每一行
#jieba分词,精确模式
line = jieba.lcut(line.strip(), cut_all=False)
#将每一行分词的结果保存在一个list中
content_list.extend(line)
fp1.close()
content_str = " ".join(content_list) #转成字符串
#print(content_str)
content_file.write(content_str+"\n") #将文本保存到tx中
label_file.write(label+"\n")
content_file.close()
label_file.close()
files.close()
存储的分词后的文本是这个样子的:
标签是这样子的:
接下来我们将训练用的分词文本和测试用的分词文本进行合并后利用word2vec训练词向量(不用过滤掉停止词):
from gensim.models import Word2Vec
from gensim.models.word2vec import PathLineSentences
import multiprocessing
import os
import sys
import logging
# 日志信息输出
program = os.path.basename(sys.argv[0])
logger = logging.getLogger(program)
logging.basicConfig(format='%(asctime)s: %(levelname)s: %(message)s')
logging.root.setLevel(level=logging.INFO)
logger.info("running %s" % ' '.join(sys.argv))
# check and process input arguments
# if len(sys.argv) # print(globals()['__doc__'] % locals())# sys.exit(1)# input_dir, outp1, outp2 = sys.argv[1:4]# 训练模型 # 输入语料目录:PathLineSentences(input_dir)# embedding size:200 共现窗口大小:10 去除出现次数10以下的词,多线程运行,迭代10次
model = Word2Vec(PathLineSentences('/content/drive/My Drive/transformer/process/Fudan/word2vec/data/'),
size=200, window=10, min_count=10,
workers=multiprocessing.cpu_count(), iter=10)
model.save('/content/drive/My Drive/transformer/process/Fudan/word2vec/model/Word2vec.w2v')
'/content/drive/My Drive/transformer/Fudan/word2vec/data/'下是train_content.txt和test_content.txt(将它们移过去的
去掉停用词:
#去除掉停用词
def _get_clean_data(self, filePath):
#先初始化停用词字典
self._get_stopwords()
sentence_list = []
with open(filePath,'r',encoding='utf-8') as fp:
lines = fp.readlines()
for line in lines:
tmp = []
words = line.strip().split(" ")
for word in words:
word = word.strip()
if word not in self.stopWordDict and word != '':
tmp.append(word)
else:
continue
sentence_list.append(tmp)
return sentence_list
#读取停用词字典
def _get_stopwords(self):
with open(os.path.join(PATH, self.stopWordSource), "r") as f:
stopWords = f.read()
stopWordList = set(stopWords.splitlines())
# 将停用词用列表的形式生成,之后查找停用词时会比较快
self.stopWordDict = dict(zip(stopWordList, list(range(len(stopWordList)))))
创建词汇表
#创建词汇表
def _get_vocaburay(self):
train_content = os.path.join(PATH, "process/Fudan/word2vec/data/train_content.txt")
sentence_list = self._get_clean_data(train_content)
#这里可以计算文本的平均长度,设置配置中的sequenceLength
#max_sequence = sum([len(s) for s in sentence_list]) / len(sentence_list)
vocab_before = []
for sentence in sentence_list:
for word in sentence:
vocab_before.append(word)
count_vocab = Counter(vocab_before) #统计每个词出现的次数
#print(len(count_vocab))
count_vocab = sorted(count_vocab.items(),key=lambda x:x[1], reverse=True) #将出现频率按从高到低排序
vocab_after = copy.deepcopy(count_vocab[:6000])
return dict(vocab_after) #返回前6000个词