1. 程式人生 > >CNN中文垃圾郵件分類(二)

CNN中文垃圾郵件分類(二)

本文整理自唐宇迪老師視訊,謝謝他!

1.思路

在上一篇部落格CNN中文垃圾郵件分類(一)中介紹了兩種預處理方式,現在來介紹第二種,先用分好詞的資料作為訓練語料,選擇前n個詞作為詞表(或者去掉出現頻率較低的詞),然後先訓練出每個詞所代表的詞向量。再根據詞表得到每封郵件中每個詞在詞表中的索引,然後按索引取出向量量堆疊起來。

2. 資料預處理

第一步同之前一樣,先去掉非中文的其它字元,然後分詞

def clean_str(string):
    string.strip('\n')
    string = re.sub(r"[^\u4e00-\u9fff]", " ", string)
    string = re.sub(r"\s{2,}"
, " ", string) return string.strip() def cut_line(line): line = clean_str(line) seg_list = jieba.cut(line) cut_words = " ".join(seg_list) return cut_words def load_data_and_labels(positive_data_file, negative_data_file): positive = [] negative = [] for line in open(positive_data_file, encoding='utf-8'
): positive.append(cut_line(line).split()) for line in open(negative_data_file, encoding='utf-8'): negative.append(cut_line(line).split()) x_text = positive + negative positive_label = [[0, 1] for _ in positive] # 構造one-hot 標籤[[0, 1], [0, 1], [0, 1], [0, 1],....] negative_label = [[1
, 0] for _ in negative] # 構造one-hot 標籤[[0, 1], [0, 1], [0, 1], [0, 1],....] y = np.concatenate([positive_label, negative_label], axis=0) return x_text, y positive_data_file = '../data/ham_100.utf8' negative_data_file = '../data/ham_100.utf8' x_text, y = load_data_and_labels(positive_data_file,negative_data_file) print(x_text)

處理後的結果形式如下:

[['溶血', '幾乎', '可以', '忽略不計', '沒什麼', '關係', '關鍵', '是', '溶血', '不過', '亞洲', '人', '大多數', '都', '是', '因子', '陽性', '所以', '溶血', '的', '概率', '很', '低', '不太', '清楚', '國內', '的', '驗血', '查不查', '因子', '米國', '這邊', '是', '查', '的', '只要', '陽性', '人家', '就', '不會', '管', '你', '是否', '溶血', '呢', '其實', '偶是'],['型', '老公', '是', '型', '按', '血型', '系統', '偶們', '這種', '組合', '是', '很', '容易', '溶血', '的', '但是', '這邊', '醫生', '提都', '沒', '跟', '偶提', '過', '估計', '是', '忽略不計', '的', '從', '網上', '看', '的', '文章', '周', '的', '時候', '去', '醫院', '建卡', '然後', '做', '第一次', '產檢', '周', '的', '時候', '再', '做', '第一次', '超', '是不是', '這樣', '呢']]

按設定的最大句子長度來Padding樣本(長度不夠的用’UNK’來填充)

def padding_sentence(sentences, padding_token='UNK', padding_sentence_length=None):
    max_padding_length = padding_sentence_length if padding_sentence_length is not \
                                                    None else max([len(sentence) for sentence in sentences])
    for i,sentence in enumerate(sentences):
        if len(sentence) < max_padding_length:
            sentence.extend([padding_token] * (max_padding_length - len(sentence)))
        else:
            sentences[i] = sentence[:max_padding_length]
    return sentences, max_padding_length

padded_sentences, max_padding_length = \
    padding_sentence(sentences=x_text, padding_sentence_length=100)
print(padded_sentences)

處理後的結果形式如下:

['浙江', '杭蕭', '鋼構', '股份', '有限公司', '為', '國內', '最大', '的', '鋼結構', '公司', '現在', '北京', '設立', '海外部', '誠徵', '致力於', '此', '的', '有識之士', '共同', '開拓', '海外', '市場', '文祕', '要求', '熟練', '運用', '常用', '辦公', '軟體', '會', '英文', '打字', '及', '日常', '英文', '商業', '信函', '的', '處理', '為', '人', '可靠', '踏實', '專案經理', '要求', '有', '進出口', '貿易', '或', '工程', '方面', '的', '經驗', '熟練', '運用', '英文', '進行', '交流', '對', '事業', '具有', '開拓精神', '對', '專業', '要求', '上', '進', '而', '不僅', '限於', '皮毛', '工作', '地點', '北京', '三環', '以內', '有意者', '請', '將', '簡歷', '電郵', '至', '請', '註明', '應聘', '的', '職位', '或', '致電', 'UNK', 'UNK', 'UNK', 'UNK', 'UNK', 'UNK', 'UNK', 'UNK', 'UNK']

將每個樣本用對應詞的詞向量堆疊的形式代替

def embedding_sentences(embedding_file='./embedding.model',
                        padded_sentences=None,
                        embedding_size=50,
                        min_count=5,
                        window=5):
    if os.path.exists(embedding_file):
        model = Word2Vec.load(embedding_file)
    else:
        model = word2vector(sentences=padded_sentences,
                            embedding_size=embedding_size,
                            min_count=min_count,
                            window=window)
    all_vectors = []
    embedding_unknown = [0 for i in range(embedding_size)]
    for sentence in padded_sentences:
        this_vector = []
        for word in sentence:
            if word in model.wv.vocab:
                this_vector.append(model[word])
            else:
                this_vector.append(embedding_unknown)
        all_vectors.append(this_vector)
    return all_vectors, len(model.wv.vocab)

x = np.array(embedded_sentences)
print(x.shape)

#結果(每個樣本都是一個[100,50]的矩陣
(200, 100, 50)

3.訓練

總體上CNN的結構不變,可以看如下對比:

沒有預訓練詞向量的版本:

with tf.device('/cpu:0'), tf.name_scope('embedding_layer'):
    self.W = tf.Variable(tf.truncated_normal([vocab_size, embedding_size])) # 隨機初始化一個詞向量矩陣
    self.embedded_chars = tf.nn.embedding_lookup(self.W, self.input_x)# 將每個樣本用詞向量堆疊的形式表示
    self.embedded_chars_expanded = tf.expand_dims(self.embedded_chars, -1)# 擴維

預先訓練好詞向量的版本:

self.embedded_chars = self.input_x #輸入的就已經是詞向量的表示形式
self.embedded_chars_expanded = tf.expand_dims(self.embedded_chars, -1)

原始碼