【实验分析】新浪文本预处理
最近写完模式识别的综述,终于能安安静静坐下来写一段时间的代码,现在的主要工作还是想把师姐的Emotion Detection的Ranking Loss的方法加到LSTM中,虽然怎么加ranking和loss还没有想好。这篇介绍的是文本的数据处理部分。
读取数据
数据这里用的是新浪新闻的数据,一个txt中保存的是一篇新闻对应的信息,包括时间、情感标签(6种)、标题和文本,具体如下。我们需要使用的就是情感标签和文本两个部分。
[url]
http://news.sina.com.cn/s/2014-10-26/110031046607.shtml
[publish_date]
2014-10-26
[emotion_votes]
moved:6 shocked:2 funny:41 sad:2 strange:1 angry:11
[headline]
余秋雨谈君子小人之别:小人想成为土豪
[body]
【余秋雨谈君子与“土豪”】著名作家余秋雨25日在武汉表示,中华民族文化的人格理想是君子。君子之道的精神核心是德,君子怀德,小人怀土,利人利他利天下为德,怀土就是时刻想到占有、占有,成为“土豪”。君子之道的阀门是有耻,要做到知耻和不耻。对伪君子,要识破文化之伪、道义之伪、风度之伪。
在这段文本中,我们需要的是情感标签和文本信息。而且它们之间都有明显的分隔符[emotion_votes]、[headline] 、[body],所以可以通过正则的方法来读取。不过其中有一个坑就是,这段文本其实有很多的换行符,但是在txt文件中打开并没有显示出有换行符,所以正则一直读的都有问题,还是后来把换行符替换了才成功的。
我们这里因为是针对长文本进行操作,所以我是将一篇文本信息读进来,当成一条进行处理。这是篇章级的操作,如果是句子级也可以一行行读进来进行处理。
def load_sina(filename):
files = os.listdir(filename)
headlinetxt = open('./data/sinatext.txt','w')
labels=[]
headline=[]
texts=[]
rule1 = r"\[emotion_votes\](.+?)\[headline\]"
rule2 = r"\[headline\](.+?)\[body\]"
rule3 = r"\[body].*"
for file in files: # 遍历文件夹
paths = filename + file
# 读取文件
infile = open(paths)
text = infile.read()
text = re.sub(r"\n", " ", text)
#得到标签
content = re.search(rule1, text)
content = re.sub(r"( )+", " ", content.group())
content = content.split(' ')[1:-1]
label = [int(emt.split(':')[1]) for emt in content]
labels.append(label)
#得到标题
content = re.search(rule2, text)
content = re.sub(r"( )+", " ", content.group())[len('[headline]')+1:-1-len('[body]')]
headline.append(content)
print(content,file=headlinetxt)
#得到内容
content = re.search(rule3, text)
content = re.sub(r"( )+", " ", content.group())[len('[body]') + 3: ]
texts.append(content)
print(content, file=headlinetxt)
infile.close()
return labels, headline, texts
文本预处理
对文本进行分词,将文本中停用词、标点之类的都去掉。其中停用词表可以在网上下载得到。
主函数
这是主函数部分,其中有些是在发现word2vec之前写的,结果后来发现word2vec自带了这部分的功能,比如删除出现次数过少的单词。
if __name__ == "__main__":
train_file = './data/sinanews/'
stopwordsfile='./data/stopwords.txt'
labels, headline, articles=load_sina(train_file)
stopwords=stopwordslist(stopwordsfile)
texts=[]
for text in articles:
# 去掉数字和标点
text=clean_text(text)
# 分词
text=segment_word(text)
# 去掉停用词
outstr = ''
for word in text:
if word not in stopwords:
if word != '\t' and len(word)>1:
outstr += word
outstr += " "
text=outstr
texts.append(text.split(' '))
#将词保存到txt
#save_texts(texts, './data/sinatextwords.txt')
#建立词典
#wordlist=build_vocab(texts) # 去掉只在一个文本中出现的单词后,词典减少了很多
#根据字典去掉句子中的不符合条件的词
#textwords=remove_words(texts, wordlist)
#word embedding
#sentences = word2vec.LineSentence('./data/sinatextwords.txt')
加载停用词表
def stopwordslist(filename):
f=open(filename)
stopwords = [line.strip() for line in f.readlines()]
f.close()
return stopwords
清除非汉字部分
标点部分使用了zhon.hanzi包。
from zhon.hanzi import punctuation
def clean_text(text):
# 清除空格
text=text.replace(' ','')
#让文本只保留汉字
text = re.sub(r"[0-9]+", "", text)
text = re.sub(r"[%s]+" % punctuation, "", text) # 去除标点
return text
分词
使用jieba对文本进行分词。
def segment_word(text):
#使用jieba对文本进行分词(精准模式)
text=jieba.cut(text)
return text
得到词向量
使用gensim得到单词的词向量。这中间有个问题是我头疼了很久,就是计算得到的词向量模型中的词是一个个字,不是一个个词,这个问题我在网上找了很久,也没有找到解决办法。无论是直接从文本中读取,还是用处理得到的text进行计算,都是这样子。后来debug的时候才发现text是一个单层列表,就是虽然分好了词,但是给我处理完又将所有词合在一起构成了一个string。而word2vec需要的是一个两层列表。后来改完了就可以了。
from gensim.models import word2vec
def word_embedding(texts,filename):
word2vecmodel = word2vec.Word2Vec(texts, size=100, min_count=2)
word2vecmodel.save(filename)
词表的建立和单词的过滤
这部分其实word2vec带了这部分的功能,但是写下来保存一下,万一以后想用呢。
def build_vocab(texts):
wordset=[list(set(text.split(' '))) for text in texts] #统计每个文章的词集合
wordcounts = Counter(itertools.chain(*wordset))
wordlist=[word[0] for word in wordcounts.most_common() if word[1]>0] #去掉只在一个文本中出现的单词
return wordlist
def remove_words(texts, wordlist):
textwords=[]
for text in texts:
words=[]
text=text.split(' ')
for word in text:
if word in wordlist:
words.append(word)
textwords.append(words)
return textwords
具体的代码可以在这里:https://git.coding.net/Mikitol/Emotion_Detection_with_LSTM.git