NLP之文本预处理

对于自然语言处理的话,预处理其实就是有那么几个固定的步骤:

分词,英文的话全部转换为小写,去除标点符号,提取词干,出去不是英文的单词,出去特殊的符号,修正错别字。

1.分词 (Tokenization)
Token 是符号,包括了单词还有标点符号两种。 Tokenization 就是把一句话或者一段话分解成单个的单词和标点。比如 I like your cat. 这句话分词之后就变成了 ['I','like','your','cat','.'] 这样的一个五元组,注意最后的标点符号也是算的。

2.提取词干(stemming)
在英文中,常常可能会有一些英文单词的各种变化,比如第三人称的单数,时态等等的变化。比如 run 可以变成runnIng,ran,runs 等等,但是我们只要他们的基本态就是run. 这个就叫做提取词干。这么做的主要目的是用统一的特征形式,特征降维以减少计算量。抽取词的词干或词根形式不一定能够表达完整语义。

在NLTK中提供了三种最常用的词干提取器接口:Porter stemmer, Lancaster Stemmer 和 Snowball Stemmer。
各有优劣,看具体文本情况。对于分类、聚类这样对于特征词语的具体形态没有要求的情况下,进行词干抽取虽然抽取后的词干可能无实际意义但是却会大大减少计算时间,提高效率。

>>> from nltk.stem.porter import PorterStemmer  
>>> porter_stemmer = PorterStemmer()  

>>> from nltk.stem.lancaster import LancasterStemmer  
>>> lancaster_stemmer = LancasterStemmer()  

>>> from nltk.stem import SnowballStemmer  
>>> snowball_stemmer = SnowballStemmer(“english”)  


>>> porter_stemmer.stem(‘maximum’)  
u’maximum’  
>>> lancaster_stemmer.stem(‘maximum’)  
‘maxim’  
>>> snowball_stemmer.stem(‘maximum’)  
u’maximum’  

>>> porter_stemmer.stem(‘presumably’)  
u’presum’  
>>> snowball_stemmer.stem(‘presumably’)  
u’presum’  
>>> lancaster_stemmer.stem(‘presumably’)  
‘presum’  

3.词形还原(Lemmatization
词形还原是把任何形式的词汇还原为一般形式,能表达完整的语义。相对而言,词干提取是简单的轻量级的词形归并方式,最后获得的结果为词干,但是可能没有实际的意义。词形还原处理相对来说比较复杂,获得结果为词的原形,能够承载一定的意义,与词干的提取相比,更具有研究和应用的价值。

比如说词干提取,假设这个词是provision得到的是provis这个没有什么实际的意义。
不过在nltk 中的Lemmatization 算法很鸡肋,基本可以理解为只有复述还原为单数的形式,一些其他的非常态的复数形式转换为单数的形式也是可以实现的。但是形容词变成名词可能会失效。

>>> from nltk.stem import WordNetLemmatizer  
>>> wordnet_lemmatizer = WordNetLemmatizer()  
>>> word = wordnet_lemmatizer.lemmatize('birds')  

bird

4.修正拼写错误

修改一些英文文本的拼写错误的话,可以用textblob 这个包

整体预处理代码:

import nltk 
from nltk.corpus import stopwords
from nltk.stem.porter import PorterStemmer
from nltk.stem import WordNetLemmatizer 


def Preprocessing(text):

    text = text.lower() # 将所有的单词转换成小写字母

    for c in string.punctuation:  # string.punctuation 所有的标点字符
        text = text.replace(c," ")  # 将标点符号转换成空格

    wordList = nltk.word_tokenize(text)  # 分词

    filtered = [w for w in wordList if w not in stopwords.words('english')] # 删除停用词

    # stem 
    ps = PorterStemmer()
    filtered = [ps.stem(w) for w in filtered]  # 提取词干
    wl = WordNetLemmatizer()   
    filtered = [wl.lemmatize(w) for w  in filtered]  # 词形还原

    return " ".join(filtered)


英文预处理整体流程代码:

#按空格进行分词,同时针对推文一些特性,去除@用户名,保留表情等一些特殊符号
tokenizer = TweetTokenizer()
for counter,rev in enumerate(reviews):
    # 去除HTML网页格式
    temp = BeautifulSoup(rev)
    text = temp.get_text()
    # 去除空格
    text = re.sub(' +',' ',text)
    test = re.sub(r'[()\[\]{}.,;:!?\<=>?@_^#$%"&*-],' ',text)
    # strip leading and trailing white space
    text = text.strip()
    # tokenize 
    tokens = tokenizer.tokenize(text)
    cleaned_reviews.append(tokens)
    if counter % round(len(reviews)/10) == 0:
        print(counter, '/', len(reviews), 'reviews cleaned')
# get list of tokens from all reviews
# 两个list变成一个list
all_tokens = [token for sublist in cleaned_reviews for token in sublist]
# 根据词频做index, 把单词转成index
counts = dict(Counter(all_tokens))
sorted_counts = sorted(counts.items(), key=operator.itemgetter(1), reverse=True)
# assign to each word an index based on its frequency in the corpus
# the most frequent word will get index equal to 1
word_to_index = dict([(tuple[0],idx+1) for idx, tuple in enumerate(sorted_counts)])
with open(path_to_IMDB + 'word_to_index_new.json', 'w') as my_file:
     json.dump(word_to_index, my_file, sort_keys=True, indent=4)

中文预处理代码:

#jieba分词和去停用词
#jieba 分词可以将我们的自定义词典导入,格式 “词” “词性” “词频”
jieba.load_userdict('data/userdict.txt')
#定义一个keyword类
class keyword(object):
    def Chinese_Stopwords(self):          #导入停用词库
        stopword=[]
        cfp=open('data/stopWord.txt','r+','utf-8')   #停用词的txt文件
        for line in cfp:
            for word in line.split():
                stopword.append(word)
        cfp.close()
        return stopword
def Word_cut_list(self,word_str):
        #利用正则表达式去掉一些一些标点符号之类的符号。
        word_str = re.sub(r'\s+', ' ', word_str)  # trans 多空格 to空格
        word_str = re.sub(r'\n+', ' ', word_str)  # trans 换行 to空格
        word_str = re.sub(r'\t+', ' ', word_str)  # trans Tab to空格
        word_str = re.sub("[\s+\.\!\/_,$%^*(+\"\']+|[+——;!,”。《》,。:“?、~@#¥%……&*()1234567①②③④)]+".decode("utf8"), "".decode("utf8"), word_str)
        wordlist = list(jieba.cut(word_str))#jieba分词
        wordlist_N = []
        chinese_stopwords=self.Chinese_Stopwords()
        for word in wordlist:
            if word not in chinese_stopwords:#词语的清洗:去停用词
                if word != '\r\n'  and word!=' ' and word != '\u3000'.decode('unicode_escape') \
                        and word!='\xa0'.decode('unicode_escape'):#词语的清洗:去全角空格
                    wordlist_N.append(word)
        return wordlist_N


#名词提取
def Word_pseg(self,word_str):  # 名词提取函数
        words = pseg.cut(word_str)
        word_list = []
        for wds in words:
            # 筛选自定义词典中的词,和各类名词,自定义词库的词在没设置词性的情况下默认为x词性,即词的flag词性为x
            if wds.flag == 'x' and wds.word != ' ' and wds.word != 'ns' \
                    or re.match(r'^n', wds.flag) != None \
                            and re.match(r'^nr', wds.flag) == None:
                word_list.append(wds.word)
        return word_list


import tensorflow.contrib.keras as kr
 
def read_file(filename):
    """读取文件数据"""
    contents, labels = [], []
    with open_file(filename) as f:
        for line in f:
            try:
                label, content = line.strip().split('\t')
                if content:
                    contents.append(list(content))#通过list把一句话分成一个个字
                    labels.append(native_content(label))
            except:
                pass
    return contents, labels
 
 
def build_vocab(train_dir, vocab_dir, vocab_size=5000):
    """根据训练集构建词汇表,存储"""
    data_train, _ = read_file(train_dir)
    all_data = []
    for content in data_train:
        all_data.extend(content)
 
    counter = Counter(all_data)
    count_pairs = counter.most_common(vocab_size - 1) #输出几个出现次数最多的元素
    words, _ = list(zip(*count_pairs)) #通过zip只取出其中的单词
    # 添加一个 <PAD> 来将所有文本pad为同一长度
    words = ['<PAD>'] + list(words)
    open_file(vocab_dir, mode='w').write('\n'.join(words) + '\n')
 
def read_vocab(vocab_dir):
    """读取词汇表"""
    # words = open_file(vocab_dir).read().strip().split('\n')
    with open_file(vocab_dir) as fp:
        # 如果是py2 则每个值都转化为unicode
        words = [native_content(_.strip()) for _ in fp.readlines()]
    word_to_id = dict(zip(words, range(len(words))))
    return words, word_to_id
 
def process_file(filename, word_to_id, cat_to_id, max_length=600):
    """将文件转换为id表示"""
    contents, labels = read_file(filename)
    data_id, label_id = [], []
    for i in range(len(contents)):
        data_id.append([word_to_id[x] for x in contents[i] if x in word_to_id])
        label_id.append(cat_to_id[labels[i]])
    # 使用keras提供的pad_sequences来将文本pad为固定长度
    x_pad = kr.preprocessing.sequence.pad_sequences(data_id, max_length)
    y_pad = kr.utils.to_categorical(label_id, num_classes=len(cat_to_id))  # 将标签转换为one-hot表示
    return x_pad, y_pad

#建立词表
text = open(path,encoding='utf-8').read().lower()
chars = set(text)
print ('total chars:', len(chars))
char_indices = dict((c, i) for i, c in enumerate(chars))
indices_char = dict((i, c) for i, c in enumerate(chars))
#kreas下运行LSTM的Input生成,在建立词表的基础上,数据向量化
print('Vectorization...')
X = np.zeros((len(sentences), maxlen, len(chars)), dtype=np.bool)
print(X)
y = np.zeros((len(sentences), len(chars)), dtype=np.bool)
for i, sentence in enumerate(sentences):
    for t, char in enumerate(sentence):
        X[i, t, char_indices[char]] = 1
    y[i, char_indices[next_chars[i]]] = 1


# 过滤词长,过滤停用词,只保留中文
def is_fine_word(word, min_length=2):
    rule = re.compile(r"^[\u4e00-\u9fa5]+$")
    if len(word) >= min_length and word not in STOP_WORDS and re.search(rule, word):
        return True
    else:
        return False

#逐字切分的处理方式,同时去掉一些常见的虚词,如“之”、“乎”、“者”、“也”。
def singCut(text):
     tex = [i.strip('\n').strip('\r').strip('。').strip(',|)|:|{|}|“|” |(|\n') for i in text]
     return list(filter(None, tex)) #去掉空字符
text = '云横秦岭家何在,雪拥蓝关马不前'
 
#虚词通用词库
stopwords = '而|何|乎|乃|且|其|若|所|为'
#去掉标点
poem = [[i.strip(') |: |?|{|}| “|” (| \n\n\r|。') for i in tex if i not in stopwords]for tex in text]
poem = list(filter(None, poem ))

预处理(去特殊符号、去停用词、分词),把词转成index(word to index), 把原文都变成数值,去掉topN词频的以及小于topM词频的,对每篇进行 truncation and padding,word2vec训练 得到 w2v_model[word] 的embedding,加入CNN作为初始值(kreas里面训练需要把每个词转成embedding这种)

训练CNN模型

https://github.com/Tixierae/deep_learning_NLP

构建词汇表

categories转成id, 读取词汇表,构建word_to_id字典(字符级别)

读入训练数据,预处理,将文本pad到固定长度

批次训练CNN(tensorflow内部会自动初始化embedding)

预测

 https://github.com/gaussic/text-classification-cnn-rnn

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章