Deep Learning 5 NLP基本过程

[TOC]

NLP

1、数据清洗 data cleaning

  • 清洗html中的残留的tag:BeautifulSoup
  • 清洗标点:正则表达式
  • 清洗常用的没有实际意义的词语(a, the):nltk中的stepwords
# import basic dependencies
from bs4 import BeautifulSoup
import re
from nltk.corpus import stopwords
# 具体使用
# html残留tag:用BeautifulSoup,实例化对象,然后通过get_text()获取
bs = BeautifulSoup(train['text_content'][index], 'lxml')
print('origin content:\n{}\n after use BeautifulSoup to clean html tag:\n{}\n', train['text_content'][index], bs.get_text())

# 标点符号:正则表达式
letters_only = re.sub('[^a-zA-Z]', ' ', bs.get_text())
print('origin content:\n{}\n after use re:\n{}\n', bs.get_text(), letters_only)

# 用nltk中导入的stopwords来删除一些常见的没有用的单词,a,the……
# 先转换成小写lower case,然后分割split,最后用nltk中的stepwords
lower_case = letters_only.lower()
words = lower_case.split()
stopwords.words('english')[:20]
words = [word for word in words if not word in stopwords.words('english')]
words[:20]

写成一个函数,然后对所有样本逐个遍历

def data_cleaning(raw):
  """
  convert raw to a string of word
  """
  # remove html tag
  text = BeautifulSoup(raw, 'lxml').get_text()
  # remove non-letters
  letters_only = re.sub('[^a-zA-Z]', ' ', text)
  # convert to lower case and spilt to words
	words = letters_only.lower().split()
  # get stopwords list
  stops = set(stopwords.words('english'))
  # remove stopwords / get non-stopwords list
  non_stopwords = [word for word in words if not word in stops]
  
  return(' '.join(non_stopwords))
# for 循环遍历
all_content = []
for i in range(0, train['content'].size):
    # If the index is evenly divisible by 1000, print a message
    if((i+1)%1000 == 0):
        print("Review %d of %d\n" % (i+1, train['content'].size))                                                                  
    all_content.append(review_to_words(train["content"][i]))

2、词干提取/词根还原 stemming/stemmer

  • 词干提取是去除单词的前后缀【「名次的复数」、「进行时」、「过去式」、「将来时」等】得到词根的过程
  • 例子:plays, played, playing -> play
  • 总结:「提取」「缩减/容易」「粒度大,模糊」「应用于搜索场景」
  • 实现:Porter、Snowball、Lancaster(可以结合nltk)
from nltk.stem import PorterStemmer
porter = PorterStemmer()

# stem -- PorterStemmer
stem_single_word = porter.stem(single_word)
stem_words = [porter.stem(word) for word in words]

3、词形还原 lemmatization/lemmatizer

  • 单词的复杂形态转变成最基础的形态。

  • 区分与词干提取,并不是简单的前后缀,是更加深层的转换【非规则的过去式、am/is/are:be等】。

    • 目标一致。词干提取和词形还原的目标均为将词的屈折形态或派生形态简化或归并为词干(stem)或原形的基础形式,都是一种对词的不同形态的统一归并的过程。
    • 结果部分交叉。词干提取和词形还原不是互斥关系,其结果是有部分交叉的。一部分词利用这两类方法都能达到相同的词形转换效果。如“dogs”的词干为“dog”,其原形也为“dog”。
    • 主流实现方法类似。目前实现词干提取和词形还原的主流实现方法均是利用语言中存在的规则或利用词典映射提取词干或获得词的原形。
    • 应用领域相似。主要应用于信息检索和文本、自然语言处理等方面,二者均是这些应用的基本步骤。
  • 例子:am, is, are -> be ;drove -> drive

  • 总结:「还原」「转变/复杂,依赖词典」「粒度小,精确」「应用于精细的文本处理如NLP, text mining」

  • 实现:利用nltk库中已经包含的英语单词词汇数据库,以及WordNet

    import nltk
    from nltk.stem import WordNetLemmatizer
    lemmatizer = WordNetLemmatizer()
    print(lemmatizer.lemmatize("blogs"))
    #Returns blogimport nltk
    from nltk.stem import WordNetLemmatizer
    lemmatizer = WordNetLemmatizer()
    print(lemmatizer.lemmatize("blogs"))
    #Returns blog
    
    from nltk.stem import WordNetLemmatizer
    wnl = WordNetLemmatizer()
      
    # lemmatization -- WordNetLemmatizer
    lemmatizered_single_word = wnl.lemmatize(single_word)
    lemmatizered_words = [wnl.lemmatize(word) for word in words]
    

4、利用Scikit-learn和词袋提取词汇表,得到每个样本的特征向量

  • 使用sklearn中的CountVectorizer来获取每个text的特征向量,作为文本的特征【这样转换的话,并没有考虑时序性,也就是说所有词袋中的所有单词都是同等的,没有先后顺序,词袋中的所有元素都各自作为一个特征的存在】
# some basic dependencies
from sklearn.feature_extraction.text import CountVectorizer
vectorizer = CountVectorizer(
  analyzer = "word",
  tokenizer = None,
  preprocessor = None,
  stop_words = None,
  max_features = 5000
) 

# get features
train_features = vectorizer.fit_transform(train_all_content)
test_features = vectorizer.fit_transform()
# transform format
train_features = train_features.toarray()
test_features = test_features.toarray()

可以用vectorizerget_feature_names()方法来看都获取了哪些feature【然后还可以自己构造一个来计算各个feature(也就是词袋中的各属性出现了几次的数据)】

print(vectorizer.get_feature_names())
  • 如果使用deeplearning,通过构造RNN来提取具有时序的特征的话,可以更好的建立前后关联
    • LSTM
    • GRU

5、原始text的一系列变形 ==> Embedding 过程 // 词向量模型(Word2Vec模型)

[可以理解成为是一个提取特征的过程] ==> 不论是通过什么进行分词或者进行词向量获取

a) 通过分词、然后对序列进行调整

  • text -> sequence ==> 分词之后,才能变成序列
  • sequence format ==> 对序列进行变形

text转换为sequence,就要引用预处理preprocessing下的text包来进行处理

from keras.preprocessing.text import Tokenizer

tokenizer = Tokenizer()
X_train_tokens = tokenizer.text_to_sequences(X_train)

产生的sequence 进行形式变换

  • pad_sequences 可以将整个序列的长度变相同**[这样需要一个参数maxlen,也就是他们相同的长度的参数,也就是他们的最大长度]**// 其他参数还有包括padding种类,这样输入模型也不会报错
from keras.preprocessing.sequence import pad_sequences

# 对上面的由text 转换的tokens进行标准化长度
X_train_pad = pad_sequence(X_train_tokens, maxlen=max_len, padding='post')

b) 利用现成的sklearn中的现有库feature_extraction 来进行提取特征

调用的一般都是CountVectorizer,TfidfVectorizer等。

from sklearn.feature_extraction

6、构建model并进行预测

  • 基本的rf random forest
  • 用feature数组作为数据集x、用label作为y
# 注意不是RandomForestRegressor
from sklearn.ensemble import RandomForestClassifier

rf = RandomForestClassifier(n_estimators=100)
rf = rf.fit(train_features, train['label'])
  • predict
prediction = rf.predict(test_features)
submission = pd.DataFrame(data={'id':test['id'], 'label':prediction})
submission.to_csv('submission.csv', index=False)

7、对于nlp的模型的构建过程

  • Embedding layer
    • 第一个参数:单词的最大数量 // 词典的总数量 [总的单词的数量 => 注意不是样本的数量]
    • 第二个参数:嵌入维度 EMBEDDING_DIM [常用100/200等] => 用于获取词袋中不同数据之间的关联
    • 第三个参数:输入的每个单词的最大长度input_length

project eg: (500, 128) => (500, 2) [第一个参数是整个词典的数量,第二个参数是词典潜入维度]

demo1

Embedding_DIM = 300
model.add(Embedding(len(list(unique_words)), Embedding_DIM, input_length=raw_max_length))

demo2

可以利用from keras.initializer import Constant 来初始化,作为Embedding layer中的一个参数,来方便初始化的过程。

model.add(Embedding(num_words, 100, embedding_initializer=Constant(embedding_matrix), input_length=MAX_LENGTH, trainable=False))
  • SpatialDropout layer (区分SpatialDropout1D,普通的Dropout):

区别:都是对图像的随意丢失,只不过

8、bert 模型

a) 三个基本参数

  • token
  • mask
  • segment

b) basic process

第一步:编码

def bert_encode(texts, tokenizer, max_len=512):
    
    all_tokens = []
    all_masks = []
    all_segments = []
    
    for text in texts:
        text = tokenizer.tokenize(text)
            
        text = text[:max_len-2]
        input_sequence = ["[CLS]"] + text + ["[SEP]"]
        pad_len = max_len - len(input_sequence)
        
        tokens = tokenizer.convert_tokens_to_ids(input_sequence)
        tokens += [0] * pad_len
        pad_masks = [1] * len(input_sequence) + [0] * pad_len
        segment_ids = [0] * max_len
        
        all_tokens.append(tokens)
        all_masks.append(pad_masks)
        all_segments.append(segment_ids)
    
    return np.array(all_tokens), np.array(all_masks), np.array(all_segments)

第二步:封装好模型

def build_model(bert_layer, max_len=512):
    input_word_ids = Input(shape=(max_len,), dtype=tf.int32, name="input_word_ids")
    input_mask = Input(shape=(max_len,), dtype=tf.int32, name="input_mask")
    segment_ids = Input(shape=(max_len,), dtype=tf.int32, name="segment_ids")

    _, sequence_output = bert_layer([input_word_ids, input_mask, segment_ids])
    clf_output = sequence_output[:, 0, :]
    
    if Dropout_num == 0:
        # Without Dropout
        out = Dense(1, activation='sigmoid')(clf_output)
    else:
        # With Dropout(Dropout_num), Dropout_num > 0
        x = Dropout(Dropout_num)(clf_output)
        out = Dense(1, activation='sigmoid')(x)

    model = Model(inputs=[input_word_ids, input_mask, segment_ids], outputs=out)
    model.compile(Adam(lr=learning_rate), loss='binary_crossentropy', metrics=['accuracy'])
    
    return model

第三步:将模型嵌套进去

【有借助hub来管理的过程】【外链的链接来管理:module_url】

  • 获取bert model,同时利用keras来接管模型,转换为Keras可以使用的。
  • 因为是相当于迁移学习[Transfer Learning]
import tensorflow_hub as hub

# from 
module_url = 'https://tfhub.dev/tensorflow/bert_en_uncased_L-24_H-1024_A-16/1'
bert_layer = hub.KerasLayer(module_url, trainable=True)

# 传入函数中,来进行获取
model_BERT = build_model(bert_layer, max_len=160)
model_BERT.summary()
Posted on Jan 28, 2020