├── CN_Corpus └── .gitkeep ├── .gitignore ├── img ├── acc_loss_model1.png ├── acc_loss_model2.png ├── wordcloud_example.png └── acc_loss_model3_cnn.png ├── const.py ├── utils.py ├── corpus_split.py ├── main_scikit.py ├── load_data.py ├── main_keras.py ├── dict └── stop_words.txt └── README.md /CN_Corpus/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | __*/ 3 | .ipynb_*/ 4 | data/ 5 | Sogou*/ 6 | Embedding/ 7 | .DS_Store 8 | *.ipynb -------------------------------------------------------------------------------- /img/acc_loss_model1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lijqhs/text-classification-cn/HEAD/img/acc_loss_model1.png -------------------------------------------------------------------------------- /img/acc_loss_model2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lijqhs/text-classification-cn/HEAD/img/acc_loss_model2.png -------------------------------------------------------------------------------- /img/wordcloud_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lijqhs/text-classification-cn/HEAD/img/wordcloud_example.png -------------------------------------------------------------------------------- /img/acc_loss_model3_cnn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lijqhs/text-classification-cn/HEAD/img/acc_loss_model3_cnn.png -------------------------------------------------------------------------------- /const.py: -------------------------------------------------------------------------------- 1 | category_labels = { 2 | 'C000008': '_08_Finance', 3 | 'C000010': '_10_IT', 4 | 'C000013': '_13_Health', 5 | 'C000014': '_14_Sports', 6 | 'C000016': '_16_Travel', 7 | 'C000020': '_20_Education', 8 | 'C000022': '_22_Recruit', 9 | 'C000023': '_23_Culture', 10 | 'C000024': '_24_Military' 11 | } 12 | -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | 2 | import matplotlib.pyplot as plt 3 | plt.style.use('ggplot') 4 | 5 | def plot_history(history): 6 | acc = history.history['acc'] 7 | val_acc = history.history['val_acc'] 8 | loss = history.history['loss'] 9 | val_loss = history.history['val_loss'] 10 | x = range(1, len(acc) + 1) 11 | 12 | plt.figure(figsize=(12, 5)) 13 | plt.subplot(1, 2, 1) 14 | plt.plot(x, acc, 'b', label='Training acc') 15 | plt.plot(x, val_acc, 'r', label='Validation acc') 16 | plt.title('Training and validation accuracy') 17 | plt.legend() 18 | plt.subplot(1, 2, 2) 19 | plt.plot(x, loss, 'b', label='Training loss') 20 | plt.plot(x, val_loss, 'r', label='Validation loss') 21 | plt.title('Training and validation loss') 22 | plt.legend() -------------------------------------------------------------------------------- /corpus_split.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | from const import category_labels 4 | 5 | def split_corpus(): 6 | # original data directory 7 | original_dataset_dir = './CN_Corpus/SogouC.reduced/Reduced' 8 | base_dir = 'data/' 9 | 10 | if (os.path.exists(base_dir)): 11 | print('`data` seems already exist.') 12 | return 13 | 14 | # make new folders 15 | os.mkdir(base_dir) 16 | train_dir = os.path.join(base_dir, 'train') 17 | os.mkdir(train_dir) 18 | test_dir = os.path.join(base_dir, 'test') 19 | os.mkdir(test_dir) 20 | 21 | # split corpus 22 | for cate in os.listdir(original_dataset_dir): 23 | cate_dir = os.path.join(original_dataset_dir, cate) 24 | file_list = os.listdir(cate_dir) 25 | print("cate: {}, len: {}".format(cate, len(file_list))) 26 | 27 | # train data 28 | fnames = file_list[:1500] 29 | dst_dir = os.path.join(train_dir, category_labels[cate]) 30 | os.mkdir(dst_dir) 31 | print("dst_dir: {}, len: {}".format(dst_dir, len(fnames))) 32 | for fname in fnames: 33 | src = os.path.join(cate_dir, fname) 34 | dst = os.path.join(dst_dir, fname) 35 | shutil.copyfile(src, dst) 36 | 37 | # test data 38 | fnames = file_list[1500:] 39 | dst_dir = os.path.join(test_dir, category_labels[cate]) 40 | os.mkdir(dst_dir) 41 | print("dst_dir: {}, len: {}".format(dst_dir, len(fnames))) 42 | for fname in fnames: 43 | src = os.path.join(cate_dir, fname) 44 | dst = os.path.join(dst_dir, fname) 45 | shutil.copyfile(src, dst) 46 | print('Corpus split DONE.') 47 | 48 | split_corpus() -------------------------------------------------------------------------------- /main_scikit.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import os 4 | import shutil 5 | import re 6 | 7 | import numpy as np 8 | from wordcloud import WordCloud 9 | import matplotlib.pyplot as plt 10 | # %matplotlib inline 11 | 12 | from sklearn.pipeline import Pipeline 13 | from sklearn.feature_extraction.text import TfidfVectorizer 14 | from sklearn.naive_bayes import MultinomialNB 15 | from sklearn.linear_model import LogisticRegression, SGDClassifier 16 | from sklearn.metrics import classification_report, confusion_matrix 17 | 18 | from load_data import preprocess, load_datasets 19 | 20 | X_train_data, y_train, X_test_data, y_test = load_datasets() 21 | 22 | wordcloud = WordCloud(scale=4, 23 | font_path='simhei.ttf', 24 | background_color='white', 25 | max_words = 100, 26 | max_font_size = 60, 27 | random_state=20).generate(X_train_data[1000]) 28 | 29 | plt.imshow(wordcloud, interpolation='bilinear') 30 | plt.axis('off') 31 | 32 | stopwords = open('dict/stop_words.txt', encoding='utf-8').read().split() 33 | 34 | # TF-IDF feature extraction 35 | tfidf_vectorizer = TfidfVectorizer(stop_words=stopwords) 36 | X_train_tfidf = tfidf_vectorizer.fit_transform(X_train_data) 37 | words = tfidf_vectorizer.get_feature_names() 38 | 39 | # naive bayes 40 | classifier = MultinomialNB() 41 | classifier.fit(X_train_tfidf, y_train) 42 | 43 | news_lastest = ["360金融旗下产品有360借条、360小微贷、360分期。360借条是360金融的核心产品,是一款无抵押、纯线上消费信贷产品,为用户提供即时到账贷款服务(通俗可以理解为“现金贷”)用户借款主要用于消费支出。从收入构成来看,360金融主要有贷款便利服务费、贷后管理服务费、融资收入、其他服务收入等构成。财报披露,营收增长主要是由于贷款便利化服务费、贷款发放后服务费和其他与贷款发放量增加相关的服务费增加。", 44 | "检方并未起诉全部涉嫌贿赂的家长,但起诉名单已有超过50人,耶鲁大学、斯坦福大学等录取率极低的名校涉案也让该事件受到了几乎全球的关注,该案甚至被称作美国“史上最大招生舞弊案”。", 45 | "俄媒称,目前尚不清楚特朗普这一言论的指向性,因为近几日,伊朗官员们都在表达力图避免与美国发生军事冲突的意愿。5月19日早些时候,伊朗革命卫队司令侯赛因·萨拉米称,伊朗只想追求和平,但并不害怕与美国发生战争。萨拉米称,“我们(伊朗)和他们(美国)之间的区别在于,美国害怕发生战争,缺乏开战的意志。”"] 46 | X_new_data = [preprocess(doc) for doc in news_lastest] 47 | X_new_tfidf = tfidf_vectorizer.transform(X_new_data) 48 | 49 | 50 | predicted = classifier.predict(X_new_tfidf) 51 | 52 | # Pipeline 53 | text_clf = Pipeline([ 54 | ('vect', TfidfVectorizer()), 55 | ('clf', MultinomialNB()), 56 | ]) 57 | 58 | text_clf.fit(X_train_data, y_train) 59 | # text_clf.predict(X_new_data) 60 | 61 | predicted = text_clf.predict(X_test_data) 62 | print(classification_report(predicted, y_test)) 63 | # confusion_matrix(predicted, y_test) 64 | 65 | # Logistic Regression 66 | text_clf_lr = Pipeline([ 67 | ('vect', TfidfVectorizer()), 68 | ('clf', LogisticRegression()), 69 | ]) 70 | text_clf_lr.fit(X_train_data, y_train) 71 | # text_clf_lr.predict(X_new_data) 72 | predicted_lr = text_clf_lr.predict(X_test_data) 73 | print(classification_report(predicted_lr, y_test)) 74 | # confusion_matrix(predicted_lr, y_test) 75 | 76 | # SVM 77 | text_clf_svm = Pipeline([ 78 | ('vect', TfidfVectorizer()), 79 | ('clf', SGDClassifier(loss='hinge', penalty='l2')), 80 | ]) 81 | text_clf_svm.fit(X_train_data, y_train) 82 | # text_clf_svm.predict(X_new_data) 83 | predicted_svm = text_clf_svm.predict(X_test_data) 84 | print(classification_report(predicted_svm, y_test)) 85 | # confusion_matrix(predicted_svm, y_test) -------------------------------------------------------------------------------- /load_data.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | import re 4 | import jieba 5 | import time 6 | import numpy as np 7 | 8 | jieba.enable_parallel() # jieba支持多进程 9 | 10 | token = "[0-9\s+\.\!\/_,$%^*()?;;:【】+\"\'\[\]\\]+|[+——!,;:。?《》、~@#¥%……&*()“”.=-]+" 11 | labels_index = {} # 记录分类标签的序号 12 | stopwords = set(open('dict/stop_words.txt', encoding='utf-8').read().split()) # 停用词 13 | 14 | 15 | # for scikit part 16 | 17 | def preprocess(text): 18 | text1 = re.sub(' ', ' ', text) 19 | str_no_punctuation = re.sub(token, ' ', text1) # 去掉标点 20 | text_list = list(jieba.cut(str_no_punctuation)) # 分词列表 21 | text_list = [item for item in text_list if item != ' '] # 去掉空格 22 | return ' '.join(text_list) 23 | 24 | 25 | def load_datasets(): 26 | # should run corpus_split.py first 27 | 28 | base_dir = 'data/' 29 | X_data = {'train':[], 'test':[]} 30 | y = {'train':[], 'test':[]} 31 | for type_name in ['train', 'test']: 32 | corpus_dir = os.path.join(base_dir, type_name) 33 | for label in os.listdir(corpus_dir): 34 | label_dir = os.path.join(corpus_dir, label) 35 | file_list = os.listdir(label_dir) 36 | print("label: {}, len: {}".format(label, len(file_list))) 37 | 38 | for fname in file_list: 39 | file_path = os.path.join(label_dir, fname) 40 | with open(file_path, encoding='gb2312', errors='ignore') as text_file: 41 | text_content = preprocess(text_file.read()) 42 | X_data[type_name].append(text_content) 43 | y[type_name].append(label) 44 | 45 | print("{} corpus len: {}\n".format(type_name, len(X_data[type_name]))) 46 | 47 | return X_data['train'], y['train'], X_data['test'], y['test'] 48 | 49 | 50 | # for keras part 51 | 52 | def preprocess_keras(text): 53 | text1 = re.sub(' ', ' ', text) 54 | str_no_punctuation = re.sub(token, ' ', text1) # 去掉标点 55 | text_list = list(jieba.cut(str_no_punctuation)) # 分词列表 56 | text_list = [item for item in text_list if item != ' ' and item not in stopwords] # 去掉空格和停用词 57 | return ' '.join(text_list) 58 | 59 | 60 | def load_raw_datasets(): 61 | labels = [] 62 | texts = [] 63 | base_dir = 'CN_Corpus/SogouC.reduced/Reduced' 64 | t1 = time.time() 65 | for cate_index, label in enumerate(os.listdir(base_dir)): 66 | label_dir = os.path.join(base_dir, label) 67 | file_list = os.listdir(label_dir) 68 | labels_index[label] = cate_index # 记录分类标签的整数标号 69 | print("label: {}, len: {}".format(label, len(file_list))) 70 | 71 | for fname in file_list: 72 | f = open(os.path.join(label_dir, fname), encoding='gb2312', errors='ignore') 73 | texts.append(preprocess_keras(f.read())) 74 | f.close() 75 | labels.append(labels_index[label]) 76 | 77 | t2 = time.time() 78 | tm_cost = t2-t1 79 | print('\nDone. {} total categories, {} total docs. cost {} seconds.'.format(len(os.listdir(base_dir)), len(texts), tm_cost)) 80 | return texts, labels 81 | 82 | def load_pre_trained(): 83 | # load pre-trained embedding model 84 | embeddings_index = {} 85 | with open('Embedding/sgns.sogou.word') as f: 86 | _, embedding_dim = f.readline().split() 87 | for line in f: 88 | values = line.split() 89 | word = values[0] 90 | coefs = np.asarray(values[1:], dtype='float32') 91 | embeddings_index[word] = coefs 92 | 93 | print('Found %s word vectors, dimension %s' % (len(embeddings_index), embedding_dim)) 94 | return embeddings_index -------------------------------------------------------------------------------- /main_keras.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import os 4 | import re 5 | import jieba 6 | import time 7 | import numpy as np 8 | from keras.preprocessing.text import Tokenizer 9 | from keras.preprocessing.sequence import pad_sequences 10 | from keras.utils import to_categorical 11 | from keras.models import Sequential, Model 12 | from keras.layers import Dense, Flatten, Embedding, Input 13 | from keras.layers import Conv1D, MaxPooling1D, Flatten 14 | 15 | from load_data import labels_index, load_raw_datasets, load_pre_trained 16 | from utils import plot_history 17 | 18 | texts, labels = load_raw_datasets() 19 | embeddings_index = load_pre_trained() 20 | 21 | MAX_SEQUENCE_LEN = 1000 # sequence length 22 | MAX_WORDS_NUM = 20000 # max words 23 | VAL_SPLIT_RATIO = 0.2 # ratio for validation 24 | EMBEDDING_DIM = 300 # embedding dimension 25 | 26 | # process datasets by keras API 27 | 28 | tokenizer = Tokenizer(num_words=MAX_WORDS_NUM) 29 | tokenizer.fit_on_texts(texts) 30 | sequences = tokenizer.texts_to_sequences(texts) 31 | 32 | word_index = tokenizer.word_index 33 | print(len(word_index)) # all token found 34 | 35 | dict_swaped = lambda _dict: {val:key for (key, val) in _dict.items()} 36 | word_dict = dict_swaped(word_index) # swap key-value 37 | data = pad_sequences(sequences, maxlen=MAX_SEQUENCE_LEN) 38 | 39 | labels_categorical = to_categorical(np.asarray(labels)) 40 | print('Shape of data tensor:', data.shape) 41 | print('Shape of label tensor:', labels_categorical.shape) 42 | 43 | indices = np.arange(data.shape[0]) 44 | np.random.shuffle(indices) 45 | data = data[indices] 46 | labels_categorical = labels_categorical[indices] 47 | 48 | # split data by ratio 49 | val_samples_num = int(VAL_SPLIT_RATIO * data.shape[0]) 50 | x_train = data[:-val_samples_num] 51 | y_train = labels_categorical[:-val_samples_num] 52 | x_val = data[-val_samples_num:] 53 | y_val = labels_categorical[-val_samples_num:] 54 | 55 | # generate embedding matrix 56 | embedding_matrix = np.zeros((MAX_WORDS_NUM+1, EMBEDDING_DIM)) # row 0 for 0 57 | for word, i in word_index.items(): 58 | embedding_vector = embeddings_index.get(word) 59 | if i < MAX_WORDS_NUM: 60 | if embedding_vector is not None: 61 | # Words not found in embedding index will be all-zeros. 62 | embedding_matrix[i] = embedding_vector 63 | 64 | # build models 65 | 66 | # model 1 without pre-trained embedding 67 | 68 | input_dim = x_train.shape[1] 69 | model1 = Sequential() 70 | model1.add(Embedding(input_dim=MAX_WORDS_NUM+1, 71 | output_dim=EMBEDDING_DIM, 72 | input_length=MAX_SEQUENCE_LEN)) 73 | model1.add(Flatten()) 74 | model1.add(Dense(64, activation='relu', input_shape=(input_dim,))) 75 | model1.add(Dense(64, activation='relu')) 76 | model1.add(Dense(len(labels_index), activation='softmax')) 77 | model1.compile(optimizer='rmsprop', 78 | loss='categorical_crossentropy', 79 | metrics=['accuracy']) 80 | 81 | history1 = model1.fit(x_train, 82 | y_train, 83 | epochs=30, 84 | batch_size=128, 85 | validation_data=(x_val, y_val)) 86 | 87 | plot_history(history1) 88 | 89 | # model 2 with pre-trained embedding 90 | 91 | model2 = Sequential() 92 | model2.add(Embedding(input_dim=MAX_WORDS_NUM+1, 93 | output_dim=EMBEDDING_DIM, 94 | weights=[embedding_matrix], 95 | input_length=MAX_SEQUENCE_LEN, 96 | trainable=False)) 97 | model2.add(Flatten()) 98 | model2.add(Dense(64, activation='relu', input_shape=(input_dim,))) 99 | model2.add(Dense(64, activation='relu')) 100 | model2.add(Dense(len(labels_index), activation='softmax')) 101 | 102 | model2.compile(optimizer='rmsprop', 103 | loss='categorical_crossentropy', 104 | metrics=['accuracy']) 105 | 106 | history2 = model2.fit(x_train, 107 | y_train, 108 | epochs=10, 109 | batch_size=128, 110 | validation_data=(x_val, y_val)) 111 | 112 | plot_history(history2) 113 | 114 | 115 | # model 3 with CNN 116 | 117 | embedding_layer = Embedding(input_dim=MAX_WORDS_NUM+1, 118 | output_dim=EMBEDDING_DIM, 119 | weights=[embedding_matrix], 120 | input_length=MAX_SEQUENCE_LEN, 121 | trainable=False) 122 | 123 | 124 | sequence_input = Input(shape=(MAX_SEQUENCE_LEN,), dtype='int32') 125 | embedded_sequences = embedding_layer(sequence_input) 126 | x = Conv1D(128, 5, activation='relu')(embedded_sequences) 127 | x = MaxPooling1D(5)(x) 128 | x = Conv1D(128, 5, activation='relu')(x) 129 | x = MaxPooling1D(5)(x) 130 | x = Conv1D(128, 5, activation='relu')(x) 131 | x = MaxPooling1D(35)(x) # global max pooling 132 | x = Flatten()(x) 133 | x = Dense(128, activation='relu')(x) 134 | preds = Dense(len(labels_index), activation='softmax')(x) 135 | 136 | model3 = Model(sequence_input, preds) 137 | model3.compile(loss='categorical_crossentropy', 138 | optimizer='rmsprop', 139 | metrics=['acc']) 140 | 141 | history3 = model3.fit(x_train, 142 | y_train, 143 | epochs=6, 144 | batch_size=128, 145 | validation_data=(x_val, y_val)) 146 | 147 | plot_history(history3) 148 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /dict/stop_words.txt: -------------------------------------------------------------------------------- 1 | 啊 2 | 阿 3 | 哎 4 | 哎呀 5 | 哎哟 6 | 唉 7 | 俺 8 | 俺们 9 | 按 10 | 按照 11 | 吧 12 | 吧哒 13 | 把 14 | 罢了 15 | 被 16 | 本 17 | 本着 18 | 比 19 | 比方 20 | 比如 21 | 鄙人 22 | 彼 23 | 彼此 24 | 边 25 | 别 26 | 别的 27 | 别说 28 | 并 29 | 并且 30 | 不比 31 | 不成 32 | 不单 33 | 不但 34 | 不独 35 | 不管 36 | 不光 37 | 不过 38 | 不仅 39 | 不拘 40 | 不论 41 | 不怕 42 | 不然 43 | 不如 44 | 不特 45 | 不惟 46 | 不问 47 | 不只 48 | 朝 49 | 朝着 50 | 趁 51 | 趁着 52 | 乘 53 | 冲 54 | 除 55 | 除此之外 56 | 除非 57 | 除了 58 | 此 59 | 此间 60 | 此外 61 | 从 62 | 从而 63 | 打 64 | 待 65 | 但 66 | 但是 67 | 当 68 | 当着 69 | 到 70 | 得 71 | 的 72 | 的话 73 | 等 74 | 等等 75 | 地 76 | 第 77 | 叮咚 78 | 对 79 | 对于 80 | 多 81 | 多少 82 | 而 83 | 而况 84 | 而且 85 | 而是 86 | 而外 87 | 而言 88 | 而已 89 | 尔后 90 | 反过来 91 | 反过来说 92 | 反之 93 | 非但 94 | 非徒 95 | 否则 96 | 嘎 97 | 嘎登 98 | 该 99 | 赶 100 | 个 101 | 各 102 | 各个 103 | 各位 104 | 各种 105 | 各自 106 | 给 107 | 根据 108 | 跟 109 | 故 110 | 故此 111 | 固然 112 | 关于 113 | 管 114 | 归 115 | 果然 116 | 果真 117 | 过 118 | 哈 119 | 哈哈 120 | 呵 121 | 和 122 | 何 123 | 何处 124 | 何况 125 | 何时 126 | 嘿 127 | 哼 128 | 哼唷 129 | 呼哧 130 | 乎 131 | 哗 132 | 还是 133 | 还有 134 | 换句话说 135 | 换言之 136 | 或 137 | 或是 138 | 或者 139 | 极了 140 | 及 141 | 及其 142 | 及至 143 | 即 144 | 即便 145 | 即或 146 | 即令 147 | 即若 148 | 即使 149 | 几 150 | 几时 151 | 己 152 | 既 153 | 既然 154 | 既是 155 | 继而 156 | 加之 157 | 假如 158 | 假若 159 | 假使 160 | 鉴于 161 | 将 162 | 较 163 | 较之 164 | 叫 165 | 接着 166 | 结果 167 | 借 168 | 紧接着 169 | 进而 170 | 尽 171 | 尽管 172 | 经 173 | 经过 174 | 就 175 | 就是 176 | 就是说 177 | 据 178 | 具体地说 179 | 具体说来 180 | 开始 181 | 开外 182 | 靠 183 | 咳 184 | 可 185 | 可见 186 | 可是 187 | 可以 188 | 况且 189 | 啦 190 | 来 191 | 来着 192 | 离 193 | 例如 194 | 哩 195 | 连 196 | 连同 197 | 两者 198 | 了 199 | 临 200 | 另 201 | 另外 202 | 另一方面 203 | 论 204 | 嘛 205 | 吗 206 | 慢说 207 | 漫说 208 | 冒 209 | 么 210 | 每 211 | 每当 212 | 们 213 | 莫若 214 | 某 215 | 某个 216 | 某些 217 | 拿 218 | 哪 219 | 哪边 220 | 哪儿 221 | 哪个 222 | 哪里 223 | 哪年 224 | 哪怕 225 | 哪天 226 | 哪些 227 | 哪样 228 | 那 229 | 那边 230 | 那儿 231 | 那个 232 | 那会儿 233 | 那里 234 | 那么 235 | 那么些 236 | 那么样 237 | 那时 238 | 那些 239 | 那样 240 | 乃 241 | 乃至 242 | 呢 243 | 能 244 | 你 245 | 你们 246 | 您 247 | 宁 248 | 宁可 249 | 宁肯 250 | 宁愿 251 | 哦 252 | 呕 253 | 啪达 254 | 旁人 255 | 呸 256 | 凭 257 | 凭借 258 | 其 259 | 其次 260 | 其二 261 | 其他 262 | 其它 263 | 其一 264 | 其余 265 | 其中 266 | 起 267 | 起见 268 | 岂但 269 | 恰恰相反 270 | 前后 271 | 前者 272 | 且 273 | 然而 274 | 然后 275 | 然则 276 | 让 277 | 人家 278 | 任 279 | 任何 280 | 任凭 281 | 如 282 | 如此 283 | 如果 284 | 如何 285 | 如其 286 | 如若 287 | 如上所述 288 | 若 289 | 若非 290 | 若是 291 | 啥 292 | 上下 293 | 尚且 294 | 设若 295 | 设使 296 | 甚而 297 | 甚么 298 | 甚至 299 | 省得 300 | 时候 301 | 什么 302 | 什么样 303 | 使得 304 | 是 305 | 是的 306 | 首先 307 | 谁 308 | 谁知 309 | 顺 310 | 顺着 311 | 似的 312 | 虽 313 | 虽然 314 | 虽说 315 | 虽则 316 | 随 317 | 随着 318 | 所 319 | 所以 320 | 他 321 | 他们 322 | 他人 323 | 它 324 | 它们 325 | 她 326 | 她们 327 | 倘 328 | 倘或 329 | 倘然 330 | 倘若 331 | 倘使 332 | 腾 333 | 替 334 | 通过 335 | 同 336 | 同时 337 | 哇 338 | 万一 339 | 往 340 | 望 341 | 为 342 | 为何 343 | 为了 344 | 为什么 345 | 为着 346 | 喂 347 | 嗡嗡 348 | 我 349 | 我们 350 | 呜 351 | 呜呼 352 | 乌乎 353 | 无论 354 | 无宁 355 | 毋宁 356 | 嘻 357 | 吓 358 | 相对而言 359 | 像 360 | 向 361 | 向着 362 | 嘘 363 | 呀 364 | 焉 365 | 沿 366 | 沿着 367 | 要 368 | 要不 369 | 要不然 370 | 要不是 371 | 要么 372 | 要是 373 | 也 374 | 也罢 375 | 也好 376 | 一 377 | 一般 378 | 一旦 379 | 一方面 380 | 一来 381 | 一切 382 | 一样 383 | 一则 384 | 依 385 | 依照 386 | 矣 387 | 以 388 | 以便 389 | 以及 390 | 以免 391 | 以至 392 | 以至于 393 | 以致 394 | 抑或 395 | 因 396 | 因此 397 | 因而 398 | 因为 399 | 哟 400 | 用 401 | 由 402 | 由此可见 403 | 由于 404 | 有 405 | 有的 406 | 有关 407 | 有些 408 | 又 409 | 于 410 | 于是 411 | 于是乎 412 | 与 413 | 与此同时 414 | 与否 415 | 与其 416 | 越是 417 | 云云 418 | 哉 419 | 再说 420 | 再者 421 | 在 422 | 在下 423 | 咱 424 | 咱们 425 | 则 426 | 怎 427 | 怎么 428 | 怎么办 429 | 怎么样 430 | 怎样 431 | 咋 432 | 照 433 | 照着 434 | 者 435 | 这 436 | 这边 437 | 这儿 438 | 这个 439 | 这会儿 440 | 这就是说 441 | 这里 442 | 这么 443 | 这么点儿 444 | 这么些 445 | 这么样 446 | 这时 447 | 这些 448 | 这样 449 | 正如 450 | 吱 451 | 之 452 | 之类 453 | 之所以 454 | 之一 455 | 只是 456 | 只限 457 | 只要 458 | 只有 459 | 至 460 | 至于 461 | 诸位 462 | 着 463 | 着呢 464 | 自 465 | 自从 466 | 自个儿 467 | 自各儿 468 | 自己 469 | 自家 470 | 自身 471 | 综上所述 472 | 总的来看 473 | 总的来说 474 | 总的说来 475 | 总而言之 476 | 总之 477 | 纵 478 | 纵令 479 | 纵然 480 | 纵使 481 | 遵照 482 | 作为 483 | 兮 484 | 呃 485 | 呗 486 | 咚 487 | 咦 488 | 喏 489 | 啐 490 | 喔唷 491 | 嗬 492 | 嗯 493 | 嗳 494 | 啊哈 495 | 啊呀 496 | 啊哟 497 | 挨次 498 | 挨个 499 | 挨家挨户 500 | 挨门挨户 501 | 挨门逐户 502 | 挨着 503 | 按理 504 | 按期 505 | 按时 506 | 按说 507 | 暗地里 508 | 暗中 509 | 暗自 510 | 昂然 511 | 八成 512 | 白白 513 | 半 514 | 梆 515 | 保管 516 | 保险 517 | 饱 518 | 背地里 519 | 背靠背 520 | 倍感 521 | 倍加 522 | 本人 523 | 本身 524 | 甭 525 | 比起 526 | 比如说 527 | 比照 528 | 毕竟 529 | 必 530 | 必定 531 | 必将 532 | 必须 533 | 便 534 | 别人 535 | 并非 536 | 并肩 537 | 并没 538 | 并没有 539 | 并排 540 | 并无 541 | 勃然 542 | 不 543 | 不必 544 | 不常 545 | 不大 546 | 不得 547 | 不得不 548 | 不得了 549 | 不得已 550 | 不迭 551 | 不定 552 | 不对 553 | 不妨 554 | 不管怎样 555 | 不会 556 | 不仅仅 557 | 不仅仅是 558 | 不经意 559 | 不可开交 560 | 不可抗拒 561 | 不力 562 | 不了 563 | 不料 564 | 不满 565 | 不免 566 | 不能不 567 | 不起 568 | 不巧 569 | 不然的话 570 | 不日 571 | 不少 572 | 不胜 573 | 不时 574 | 不是 575 | 不同 576 | 不能 577 | 不要 578 | 不外 579 | 不外乎 580 | 不下 581 | 不限 582 | 不消 583 | 不已 584 | 不亦乐乎 585 | 不由得 586 | 不再 587 | 不择手段 588 | 不怎么 589 | 不曾 590 | 不知不觉 591 | 不止 592 | 不止一次 593 | 不至于 594 | 才 595 | 才能 596 | 策略地 597 | 差不多 598 | 差一点 599 | 常 600 | 常常 601 | 常言道 602 | 常言说 603 | 常言说得好 604 | 长此下去 605 | 长话短说 606 | 长期以来 607 | 长线 608 | 敞开儿 609 | 彻夜 610 | 陈年 611 | 趁便 612 | 趁机 613 | 趁热 614 | 趁势 615 | 趁早 616 | 成年 617 | 成年累月 618 | 成心 619 | 乘机 620 | 乘胜 621 | 乘势 622 | 乘隙 623 | 乘虚 624 | 诚然 625 | 迟早 626 | 充分 627 | 充其极 628 | 充其量 629 | 抽冷子 630 | 臭 631 | 初 632 | 出 633 | 出来 634 | 出去 635 | 除此 636 | 除此而外 637 | 除此以外 638 | 除开 639 | 除去 640 | 除却 641 | 除外 642 | 处处 643 | 川流不息 644 | 传 645 | 传说 646 | 传闻 647 | 串行 648 | 纯 649 | 纯粹 650 | 此后 651 | 此中 652 | 次第 653 | 匆匆 654 | 从不 655 | 从此 656 | 从此以后 657 | 从古到今 658 | 从古至今 659 | 从今以后 660 | 从宽 661 | 从来 662 | 从轻 663 | 从速 664 | 从头 665 | 从未 666 | 从无到有 667 | 从小 668 | 从新 669 | 从严 670 | 从优 671 | 从早到晚 672 | 从中 673 | 从重 674 | 凑巧 675 | 粗 676 | 存心 677 | 达旦 678 | 打从 679 | 打开天窗说亮话 680 | 大 681 | 大不了 682 | 大大 683 | 大抵 684 | 大都 685 | 大多 686 | 大凡 687 | 大概 688 | 大家 689 | 大举 690 | 大略 691 | 大面儿上 692 | 大事 693 | 大体 694 | 大体上 695 | 大约 696 | 大张旗鼓 697 | 大致 698 | 呆呆地 699 | 带 700 | 殆 701 | 待到 702 | 单 703 | 单纯 704 | 单单 705 | 但愿 706 | 弹指之间 707 | 当场 708 | 当儿 709 | 当即 710 | 当口儿 711 | 当然 712 | 当庭 713 | 当头 714 | 当下 715 | 当真 716 | 当中 717 | 倒不如 718 | 倒不如说 719 | 倒是 720 | 到处 721 | 到底 722 | 到了儿 723 | 到目前为止 724 | 到头 725 | 到头来 726 | 得起 727 | 得天独厚 728 | 的确 729 | 等到 730 | 叮当 731 | 顶多 732 | 定 733 | 动不动 734 | 动辄 735 | 陡然 736 | 都 737 | 独 738 | 独自 739 | 断然 740 | 顿时 741 | 多次 742 | 多多 743 | 多多少少 744 | 多多益善 745 | 多亏 746 | 多年来 747 | 多年前 748 | 而后 749 | 而论 750 | 而又 751 | 尔等 752 | 二话不说 753 | 二话没说 754 | 反倒 755 | 反倒是 756 | 反而 757 | 反手 758 | 反之亦然 759 | 反之则 760 | 方 761 | 方才 762 | 方能 763 | 放量 764 | 非常 765 | 非得 766 | 分期 767 | 分期分批 768 | 分头 769 | 奋勇 770 | 愤然 771 | 风雨无阻 772 | 逢 773 | 弗 774 | 甫 775 | 嘎嘎 776 | 该当 777 | 概 778 | 赶快 779 | 赶早不赶晚 780 | 敢 781 | 敢情 782 | 敢于 783 | 刚 784 | 刚才 785 | 刚好 786 | 刚巧 787 | 高低 788 | 格外 789 | 隔日 790 | 隔夜 791 | 个人 792 | 各式 793 | 更 794 | 更加 795 | 更进一步 796 | 更为 797 | 公然 798 | 共 799 | 共总 800 | 够瞧的 801 | 姑且 802 | 古来 803 | 故而 804 | 故意 805 | 固 806 | 怪 807 | 怪不得 808 | 惯常 809 | 光 810 | 光是 811 | 归根到底 812 | 归根结底 813 | 过于 814 | 毫不 815 | 毫无 816 | 毫无保留地 817 | 毫无例外 818 | 好在 819 | 何必 820 | 何尝 821 | 何妨 822 | 何苦 823 | 何乐而不为 824 | 何须 825 | 何止 826 | 很 827 | 很多 828 | 很少 829 | 轰然 830 | 后来 831 | 呼啦 832 | 忽地 833 | 忽然 834 | 互 835 | 互相 836 | 哗啦 837 | 话说 838 | 还 839 | 恍然 840 | 会 841 | 豁然 842 | 活 843 | 伙同 844 | 或多或少 845 | 或许 846 | 基本 847 | 基本上 848 | 基于 849 | 极 850 | 极大 851 | 极度 852 | 极端 853 | 极力 854 | 极其 855 | 极为 856 | 急匆匆 857 | 即将 858 | 即刻 859 | 即是说 860 | 几度 861 | 几番 862 | 几乎 863 | 几经 864 | 既…又 865 | 继之 866 | 加上 867 | 加以 868 | 间或 869 | 简而言之 870 | 简言之 871 | 简直 872 | 见 873 | 将才 874 | 将近 875 | 将要 876 | 交口 877 | 较比 878 | 较为 879 | 接连不断 880 | 接下来 881 | 皆可 882 | 截然 883 | 截至 884 | 藉以 885 | 借此 886 | 借以 887 | 届时 888 | 仅 889 | 仅仅 890 | 谨 891 | 进来 892 | 进去 893 | 近 894 | 近几年来 895 | 近来 896 | 近年来 897 | 尽管如此 898 | 尽可能 899 | 尽快 900 | 尽量 901 | 尽然 902 | 尽如人意 903 | 尽心竭力 904 | 尽心尽力 905 | 尽早 906 | 精光 907 | 经常 908 | 竟 909 | 竟然 910 | 究竟 911 | 就此 912 | 就地 913 | 就算 914 | 居然 915 | 局外 916 | 举凡 917 | 据称 918 | 据此 919 | 据实 920 | 据说 921 | 据我所知 922 | 据悉 923 | 具体来说 924 | 决不 925 | 决非 926 | 绝 927 | 绝不 928 | 绝顶 929 | 绝对 930 | 绝非 931 | 均 932 | 喀 933 | 看 934 | 看来 935 | 看起来 936 | 看上去 937 | 看样子 938 | 可好 939 | 可能 940 | 恐怕 941 | 快 942 | 快要 943 | 来不及 944 | 来得及 945 | 来讲 946 | 来看 947 | 拦腰 948 | 牢牢 949 | 老 950 | 老大 951 | 老老实实 952 | 老是 953 | 累次 954 | 累年 955 | 理当 956 | 理该 957 | 理应 958 | 历 959 | 立 960 | 立地 961 | 立刻 962 | 立马 963 | 立时 964 | 联袂 965 | 连连 966 | 连日 967 | 连日来 968 | 连声 969 | 连袂 970 | 临到 971 | 另方面 972 | 另行 973 | 另一个 974 | 路经 975 | 屡 976 | 屡次 977 | 屡次三番 978 | 屡屡 979 | 缕缕 980 | 率尔 981 | 率然 982 | 略 983 | 略加 984 | 略微 985 | 略为 986 | 论说 987 | 马上 988 | 蛮 989 | 满 990 | 没 991 | 没有 992 | 每逢 993 | 每每 994 | 每时每刻 995 | 猛然 996 | 猛然间 997 | 莫 998 | 莫不 999 | 莫非 1000 | 莫如 1001 | 默默地 1002 | 默然 1003 | 呐 1004 | 那末 1005 | 奈 1006 | 难道 1007 | 难得 1008 | 难怪 1009 | 难说 1010 | 内 1011 | 年复一年 1012 | 凝神 1013 | 偶而 1014 | 偶尔 1015 | 怕 1016 | 砰 1017 | 碰巧 1018 | 譬如 1019 | 偏偏 1020 | 乒 1021 | 平素 1022 | 颇 1023 | 迫于 1024 | 扑通 1025 | 其后 1026 | 其实 1027 | 奇 1028 | 齐 1029 | 起初 1030 | 起来 1031 | 起首 1032 | 起头 1033 | 起先 1034 | 岂 1035 | 岂非 1036 | 岂止 1037 | 迄 1038 | 恰逢 1039 | 恰好 1040 | 恰恰 1041 | 恰巧 1042 | 恰如 1043 | 恰似 1044 | 千 1045 | 万 1046 | 千万 1047 | 千万千万 1048 | 切 1049 | 切不可 1050 | 切莫 1051 | 切切 1052 | 切勿 1053 | 窃 1054 | 亲口 1055 | 亲身 1056 | 亲手 1057 | 亲眼 1058 | 亲自 1059 | 顷 1060 | 顷刻 1061 | 顷刻间 1062 | 顷刻之间 1063 | 请勿 1064 | 穷年累月 1065 | 取道 1066 | 去 1067 | 权时 1068 | 全都 1069 | 全力 1070 | 全年 1071 | 全然 1072 | 全身心 1073 | 然 1074 | 人人 1075 | 仍 1076 | 仍旧 1077 | 仍然 1078 | 日复一日 1079 | 日见 1080 | 日渐 1081 | 日益 1082 | 日臻 1083 | 如常 1084 | 如此等等 1085 | 如次 1086 | 如今 1087 | 如期 1088 | 如前所述 1089 | 如上 1090 | 如下 1091 | 汝 1092 | 三番两次 1093 | 三番五次 1094 | 三天两头 1095 | 瑟瑟 1096 | 沙沙 1097 | 上 1098 | 上来 1099 | 上去 1100 | 一. 1101 | 一一 1102 | 一下 1103 | 一个 1104 | 一些 1105 | 一何 1106 | 一则通过 1107 | 一天 1108 | 一定 1109 | 一时 1110 | 一次 1111 | 一片 1112 | 一番 1113 | 一直 1114 | 一致 1115 | 一起 1116 | 一转眼 1117 | 一边 1118 | 一面 1119 | 上升 1120 | 上述 1121 | 上面 1122 | 下 1123 | 下列 1124 | 下去 1125 | 下来 1126 | 下面 1127 | 不一 1128 | 不久 1129 | 不变 1130 | 不可 1131 | 不够 1132 | 不尽 1133 | 不尽然 1134 | 不敢 1135 | 不断 1136 | 不若 1137 | 不足 1138 | 与其说 1139 | 专门 1140 | 且不说 1141 | 且说 1142 | 严格 1143 | 严重 1144 | 个别 1145 | 中小 1146 | 中间 1147 | 丰富 1148 | 为主 1149 | 为什麽 1150 | 为止 1151 | 为此 1152 | 主张 1153 | 主要 1154 | 举行 1155 | 乃至于 1156 | 之前 1157 | 之后 1158 | 之後 1159 | 也就是说 1160 | 也是 1161 | 了解 1162 | 争取 1163 | 二来 1164 | 云尔 1165 | 些 1166 | 亦 1167 | 产生 1168 | 人 1169 | 人们 1170 | 什麽 1171 | 今 1172 | 今后 1173 | 今天 1174 | 今年 1175 | 今後 1176 | 介于 1177 | 从事 1178 | 他是 1179 | 他的 1180 | 代替 1181 | 以上 1182 | 以下 1183 | 以为 1184 | 以前 1185 | 以后 1186 | 以外 1187 | 以後 1188 | 以故 1189 | 以期 1190 | 以来 1191 | 任务 1192 | 企图 1193 | 伟大 1194 | 似乎 1195 | 但凡 1196 | 何以 1197 | 余外 1198 | 你是 1199 | 你的 1200 | 使 1201 | 使用 1202 | 依据 1203 | 依靠 1204 | 便于 1205 | 促进 1206 | 保持 1207 | 做到 1208 | 傥然 1209 | 儿 1210 | 允许 1211 | 元/吨 1212 | 先不先 1213 | 先后 1214 | 先後 1215 | 先生 1216 | 全体 1217 | 全部 1218 | 全面 1219 | 共同 1220 | 具体 1221 | 具有 1222 | 兼之 1223 | 再 1224 | 再其次 1225 | 再则 1226 | 再有 1227 | 再次 1228 | 再者说 1229 | 决定 1230 | 准备 1231 | 凡 1232 | 凡是 1233 | 出于 1234 | 出现 1235 | 分别 1236 | 则甚 1237 | 别处 1238 | 别是 1239 | 别管 1240 | 前此 1241 | 前进 1242 | 前面 1243 | 加入 1244 | 加强 1245 | 十分 1246 | 即如 1247 | 却 1248 | 却不 1249 | 原来 1250 | 又及 1251 | 及时 1252 | 双方 1253 | 反应 1254 | 反映 1255 | 取得 1256 | 受到 1257 | 变成 1258 | 另悉 1259 | 只 1260 | 只当 1261 | 只怕 1262 | 只消 1263 | 叫做 1264 | 召开 1265 | 各人 1266 | 各地 1267 | 各级 1268 | 合理 1269 | 同一 1270 | 同样 1271 | 后 1272 | 后者 1273 | 后面 1274 | 向使 1275 | 周围 1276 | 呵呵 1277 | 咧 1278 | 唯有 1279 | 啷当 1280 | 喽 1281 | 嗡 1282 | 嘿嘿 1283 | 因了 1284 | 因着 1285 | 在于 1286 | 坚决 1287 | 坚持 1288 | 处在 1289 | 处理 1290 | 复杂 1291 | 多么 1292 | 多数 1293 | 大力 1294 | 大多数 1295 | 大批 1296 | 大量 1297 | 失去 1298 | 她是 1299 | 她的 1300 | 好 1301 | 好的 1302 | 好象 1303 | 如同 1304 | 如是 1305 | 始而 1306 | 存在 1307 | 孰料 1308 | 孰知 1309 | 它们的 1310 | 它是 1311 | 它的 1312 | 安全 1313 | 完全 1314 | 完成 1315 | 实现 1316 | 实际 1317 | 宣布 1318 | 容易 1319 | 密切 1320 | 对应 1321 | 对待 1322 | 对方 1323 | 对比 1324 | 小 1325 | 少数 1326 | 尔 1327 | 尔尔 1328 | 尤其 1329 | 就是了 1330 | 就要 1331 | 属于 1332 | 左右 1333 | 巨大 1334 | 巩固 1335 | 已 1336 | 已矣 1337 | 已经 1338 | 巴 1339 | 巴巴 1340 | 帮助 1341 | 并不 1342 | 并不是 1343 | 广大 1344 | 广泛 1345 | 应当 1346 | 应用 1347 | 应该 1348 | 庶乎 1349 | 庶几 1350 | 开展 1351 | 引起 1352 | 强烈 1353 | 强调 1354 | 归齐 1355 | 当前 1356 | 当地 1357 | 当时 1358 | 形成 1359 | 彻底 1360 | 彼时 1361 | 往往 1362 | 後来 1363 | 後面 1364 | 得了 1365 | 得出 1366 | 得到 1367 | 心里 1368 | 必然 1369 | 必要 1370 | 怎奈 1371 | 怎麽 1372 | 总是 1373 | 总结 1374 | 您们 1375 | 您是 1376 | 惟其 1377 | 意思 1378 | 愿意 1379 | 成为 1380 | 我是 1381 | 我的 1382 | 或则 1383 | 或曰 1384 | 战斗 1385 | 所在 1386 | 所幸 1387 | 所有 1388 | 所谓 1389 | 扩大 1390 | 掌握 1391 | 接著 1392 | 数/ 1393 | 整个 1394 | 方便 1395 | 方面 1396 | 无 1397 | 无法 1398 | 既往 1399 | 明显 1400 | 明确 1401 | 是不是 1402 | 是以 1403 | 是否 1404 | 显然 1405 | 显著 1406 | 普通 1407 | 普遍 1408 | 曾 1409 | 曾经 1410 | 替代 1411 | 最 1412 | 最后 1413 | 最大 1414 | 最好 1415 | 最後 1416 | 最近 1417 | 最高 1418 | 有利 1419 | 有力 1420 | 有及 1421 | 有所 1422 | 有效 1423 | 有时 1424 | 有点 1425 | 有的是 1426 | 有着 1427 | 有著 1428 | 末##末 1429 | 本地 1430 | 来自 1431 | 来说 1432 | 构成 1433 | 某某 1434 | 根本 1435 | 欢迎 1436 | 欤 1437 | 正值 1438 | 正在 1439 | 正巧 1440 | 正常 1441 | 正是 1442 | 此地 1443 | 此处 1444 | 此时 1445 | 此次 1446 | 每个 1447 | 每天 1448 | 每年 1449 | 比及 1450 | 比较 1451 | 没奈何 1452 | 注意 1453 | 深入 1454 | 清楚 1455 | 满足 1456 | 然後 1457 | 特别是 1458 | 特殊 1459 | 特点 1460 | 犹且 1461 | 犹自 1462 | 现代 1463 | 现在 1464 | 甚且 1465 | 甚或 1466 | 甚至于 1467 | 用来 1468 | 由是 1469 | 由此 1470 | 目前 1471 | 直到 1472 | 直接 1473 | 相似 1474 | 相信 1475 | 相反 1476 | 相同 1477 | 相对 1478 | 相应 1479 | 相当 1480 | 相等 1481 | 看出 1482 | 看到 1483 | 看看 1484 | 看见 1485 | 真是 1486 | 真正 1487 | 眨眼 1488 | 矣乎 1489 | 矣哉 1490 | 知道 1491 | 确定 1492 | 种 1493 | 积极 1494 | 移动 1495 | 突出 1496 | 突然 1497 | 立即 1498 | 竟而 1499 | 第二 1500 | 类如 1501 | 练习 1502 | 组成 1503 | 结合 1504 | 继后 1505 | 继续 1506 | 维持 1507 | 考虑 1508 | 联系 1509 | 能否 1510 | 能够 1511 | 自后 1512 | 自打 1513 | 至今 1514 | 至若 1515 | 致 1516 | 般的 1517 | 良好 1518 | 若夫 1519 | 若果 1520 | 范围 1521 | 莫不然 1522 | 获得 1523 | 行为 1524 | 行动 1525 | 表明 1526 | 表示 1527 | 要求 1528 | 规定 1529 | 觉得 1530 | 譬喻 1531 | 认为 1532 | 认真 1533 | 认识 1534 | 许多 1535 | 设或 1536 | 诚如 1537 | 说明 1538 | 说来 1539 | 说说 1540 | 诸 1541 | 诸如 1542 | 谁人 1543 | 谁料 1544 | 贼死 1545 | 赖以 1546 | 距 1547 | 转动 1548 | 转变 1549 | 转贴 1550 | 达到 1551 | 迅速 1552 | 过去 1553 | 过来 1554 | 运用 1555 | 还要 1556 | 这一来 1557 | 这次 1558 | 这点 1559 | 这种 1560 | 这般 1561 | 这麽 1562 | 进入 1563 | 进步 1564 | 进行 1565 | 适应 1566 | 适当 1567 | 适用 1568 | 逐步 1569 | 逐渐 1570 | 通常 1571 | 造成 1572 | 遇到 1573 | 遭到 1574 | 遵循 1575 | 避免 1576 | 那般 1577 | 那麽 1578 | 部分 1579 | 采取 1580 | 里面 1581 | 重大 1582 | 重新 1583 | 重要 1584 | 针对 1585 | 问题 1586 | 防止 1587 | 附近 1588 | 限制 1589 | 随后 1590 | 随时 1591 | 随著 1592 | 难道说 1593 | 集中 1594 | 需要 1595 | 非特 1596 | 非独 1597 | 高兴 1598 | 若果 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Text Classification 3 | 4 | 文本分类(Text Classification)是自然语言处理中的一个重要应用技术,根据文档的内容或主题,自动识别文档所属的预先定义的类别标签。文本分类是很多应用场景的基础,比如垃圾邮件识别,舆情分析,情感识别,新闻自动分类,智能客服机器人的知识库分类等等。本文分为两个部分: 5 | 6 | - Part 1: 基于scikit-learn机器学习Python库,对比几个传统机器学习方法的文本分类。[Blog Post](https://lijqhs.github.io/2019/05/text-classification-scikit-learn/) 7 | - Part 2: 基于预训练词向量模型,使用Keras工具进行文本分类,用到了CNN。[Blog Post](https://lijqhs.github.io/2019/05/text-classification-pretrained-keras-cnn/) 8 | 9 | 本文语料:[下载链接](https://pan.baidu.com/s/1Z4nMw-Eem0C71m8djI-OAA),密码:P9M4。更多新闻标注语料,[下载链接](http://www.sogou.com/labs/resource/list_news.php)。 10 | 11 | 12 | 预训练词向量模型来自[GitHub:Chinese Word Vectors 上百种预训练中文词向量](https://github.com/Embedding/Chinese-Word-Vectors),下载地址:[Sogou News 300d](https://pan.baidu.com/s/1tUghuTno5yOvOx4LXA9-wg)。 13 | 14 | 15 | 16 | - [Text Classification](#text-classification) 17 | - [Part 1: 基于scikit-learn机器学习的文本分类方法](#part-1-基于scikit-learn机器学习的文本分类方法) 18 | - [1. 语料预处理](#1-语料预处理) 19 | - [2. 生成训练集和测试集](#2-生成训练集和测试集) 20 | - [生成数据集](#生成数据集) 21 | - [3. 文本特征提取:TF-IDF](#3-文本特征提取tf-idf) 22 | - [4. 构建分类器](#4-构建分类器) 23 | - [Benchmark: 朴素贝叶斯分类器](#benchmark-朴素贝叶斯分类器) 24 | - [对新文本应用分类](#对新文本应用分类) 25 | - [5. 分类器的评估](#5-分类器的评估) 26 | - [构建Logistic Regression分类器](#构建logistic-regression分类器) 27 | - [构建SVM分类器](#构建svm分类器) 28 | - [Part 2: 基于预训练模型的CNN文本分类方法-Keras](#part-2-基于预训练模型的cnn文本分类方法-keras) 29 | - [1. 读取语料](#1-读取语料) 30 | - [2. 加载预训练词向量模型](#2-加载预训练词向量模型) 31 | - [3. 使用Keras对语料进行处理](#3-使用keras对语料进行处理) 32 | - [4. 定义词嵌入矩阵](#4-定义词嵌入矩阵) 33 | - [Embedding Layer](#embedding-layer) 34 | - [5. 构建模型](#5-构建模型) 35 | - [参考资料](#参考资料) 36 | 37 | 38 | 39 | ## Part 1: 基于scikit-learn机器学习的文本分类方法 40 | 41 | 基于scikit-learn机器学习的中文文本分类主要分为以下步骤: 42 | 43 | 1. 语料预处理 44 | 2. 生成训练集和测试集 45 | 3. 文本特征提取:TF-IDF 46 | 4. 构建分类器 47 | 5. 分类器的评估 48 | 49 | ### 1. 语料预处理 50 | 51 | 定义搜狗新闻文本标签的名称,类似`C000008`这样的标签是语料的子目录,在网上搜到标签对应的新闻类别,为了便于理解,定义了这个映射词典,并保留原有编号信息。在网上搜索下载`搜狗分类新闻.20061127.zip`语料并解压至`CN_Corpus`目录下,解压之后目录结构为: 52 | 53 | ``` 54 | CN_Corpus 55 | └─SogouC.reduced 56 | └─Reduced 57 | ├─C000008 58 | ├─C000010 59 | ├─C000013 60 | ├─C000014 61 | ├─C000016 62 | ├─C000020 63 | ├─C000022 64 | ├─C000023 65 | └─C000024 66 | ``` 67 | 68 | ```python 69 | category_labels = { 70 | 'C000008': '_08_Finance', 71 | 'C000010': '_10_IT', 72 | 'C000013': '_13_Health', 73 | 'C000014': '_14_Sports', 74 | 'C000016': '_16_Travel', 75 | 'C000020': '_20_Education', 76 | 'C000022': '_22_Recruit', 77 | 'C000023': '_23_Culture', 78 | 'C000024': '_24_Military' 79 | } 80 | ``` 81 | 82 | 下面进行语料的切分,将每个类别的前80%作为训练语料,后20%作为测试语料。切分完之后的语料目录如下: 83 | ``` 84 | data 85 | ├─test 86 | │ ├─_08_Finance 87 | │ ├─_10_IT 88 | │ ├─_13_Health 89 | │ ├─_14_Sports 90 | │ ├─_16_Travel 91 | │ ├─_20_Education 92 | │ ├─_22_Recruit 93 | │ ├─_23_Culture 94 | │ └─_24_Military 95 | └─train 96 | ├─_08_Finance 97 | ├─_10_IT 98 | ├─_13_Health 99 | ├─_14_Sports 100 | ├─_16_Travel 101 | ├─_20_Education 102 | ├─_22_Recruit 103 | ├─_23_Culture 104 | └─_24_Military 105 | ``` 106 | 107 | 108 | ### 2. 生成训练集和测试集 109 | 110 | #### 生成数据集 111 | 112 | 从上面切分好的语料目录中读取文本并进行分词预处理,输出:训练语料数据(`X_train_data`)、训练语料标签(`y_train`)、测试语料数据(`X_test_data`)、测试语料标签(`y_test`)。 113 | 114 | 115 | ```python 116 | X_train_data, y_train, X_test_data, y_test = load_datasets() 117 | ``` 118 | 119 | label: _08_Finance, len: 1500 120 | label: _10_IT, len: 1500 121 | label: _13_Health, len: 1500 122 | label: _14_Sports, len: 1500 123 | label: _16_Travel, len: 1500 124 | label: _20_Education, len: 1500 125 | label: _22_Recruit, len: 1500 126 | label: _23_Culture, len: 1500 127 | label: _24_Military, len: 1500 128 | train corpus len: 13500 129 | 130 | label: _08_Finance, len: 490 131 | label: _10_IT, len: 490 132 | label: _13_Health, len: 490 133 | label: _14_Sports, len: 490 134 | label: _16_Travel, len: 490 135 | label: _20_Education, len: 490 136 | label: _22_Recruit, len: 490 137 | label: _23_Culture, len: 490 138 | label: _24_Military, len: 490 139 | test corpus len: 4410 140 | 141 | 数据集的形式如下: 142 | 143 | ```python 144 | X_train_data[1000] 145 | ``` 146 | 147 | '新华网 上海 月 日电 记者 黄庭钧 继 日 人民币 兑 美元 中间价 突破 关口 创 历史 新高 后 日 人民币 兑 美元汇率 继续 攀升 中国外汇交易中心 日 公布 的 中间价 为 再刷 历史 新高 大有 逼近 和 突破 心理 关口 之势 据 兴业银行 资金 营运 中心 交易员 余屹 介绍 人民币 兑 美元汇率 日 走势 继续 表现 强劲 竞价 交易 以 开盘 后 最低 曾 回到 最高 则 触及 距 关口 仅 一步之遥 收报 而 询价 交易 亦 表现 不俗 以 开盘 后 曾经 走低 到 最高 仅触 到 截至 时 分 报收 虽然 全日 波幅 较窄 但 均 在 下方 有 跃跃欲试 关口 之 态势 完' 148 | 149 | ```python 150 | y_train[1000] 151 | ``` 152 | 153 | '_08_Finance' 154 | 155 | ![wordcloud_example](img/wordcloud_example.png) 156 | 157 | ### 3. 文本特征提取:TF-IDF 158 | 159 | 这个步骤将文档信息,也即每篇新闻被分好词之后的词集合,转为为基于词频-你文档词频(TF-IDF)的向量,向量的每个元素都是对应于某个词在这个文档中的TF-IDF值,在不同文档中,同一词的TF-IDF是不一样的。所有文档的TF-IDF向量堆放在一起就组成了一个TF-IDF矩阵。注意到这里应该包含了除停用词之外的所有词的TF-IDF值,词的个数构成了向量的维度。 160 | 161 | 用`TfidfVectorizer`将文档集合转为`TF-IDF`矩阵。注意到前面我们将文本做了分词并用空格隔开。如果是英文,本身就是空格隔开的,而英文的分词(Tokenizing)是包含在特征提取器中的,不需要分词这一步骤。下面我们在得到了分类器之后,使用新文本进行分类预测时,也是需要先做一下中文分词的。 162 | 163 | ```python 164 | stopwords = open('dict/stop_words.txt', encoding='utf-8').read().split() 165 | 166 | # TF-IDF feature extraction 167 | tfidf_vectorizer = TfidfVectorizer(stop_words=stopwords) 168 | X_train_tfidf = tfidf_vectorizer.fit_transform(X_train_data) 169 | words = tfidf_vectorizer.get_feature_names() 170 | ``` 171 | 172 | ```python 173 | X_train_tfidf.shape 174 | ``` 175 | 176 | (13500, 223094) 177 | 178 | ```python 179 | len(words) 180 | ``` 181 | 182 | 223094 183 | 184 | ### 4. 构建分类器 185 | 186 | #### Benchmark: 朴素贝叶斯分类器 187 | 188 | 得到了训练样本的文本特征,现在可以训练出一个分类器,以用来对新的新闻文本进行分类。`scikit-learn`中提供了多种分类器,其中[朴素贝叶斯](https://scikit-learn.org/stable/modules/naive_bayes.html#naive-bayes)是一个很好的基准,有多个版本的朴素贝叶斯分类器,其中[`MultinomialNB`](https://scikit-learn.org/stable/modules/naive_bayes.html#multinomial-naive-bayes)比较适合于文本分类。 189 | 190 | ```python 191 | classifier = MultinomialNB() 192 | classifier.fit(X_train_tfidf, y_train) 193 | ``` 194 | 195 | MultinomialNB(alpha=1.0, class_prior=None, fit_prior=True) 196 | 197 | #### 对新文本应用分类 198 | 199 | 对新的文本需要进行分类,那么只需将上面的`tfidf_vectorizer`应用在新的文本上,调用`transform`方法而不是`fit_transform`,将新的文本转换为`TF-IDF`特征,然后再调用分类器的`predict`,得到分类。 200 | 201 | 下面新闻节选自腾讯新闻网,原文地址: 202 | 203 | - [周鸿祎金融梦,营收20亿元直逼趣店,净利润同比增340%](https://new.qq.com/omn/20190521/20190521A08M6V.html) 204 | - [录取率低过5%的美国名校,为何“花钱”就能上?](https://new.qq.com/omn/20190503/20190503A03SIG.html) 205 | - [特朗普:伊朗对美军动武将会“灭亡”](https://new.qq.com/omn/20190520/20190520A0D4LA.html) 206 | 207 | ```python 208 | news_lastest = ["360金融旗下产品有360借条、360小微贷、360分期。360借条是360金融的核心产品,是一款无抵押、纯线上消费信贷产品,为用户提供即时到账贷款服务(通俗可以理解为“现金贷”)用户借款主要用于消费支出。从收入构成来看,360金融主要有贷款便利服务费、贷后管理服务费、融资收入、其他服务收入等构成。财报披露,营收增长主要是由于贷款便利化服务费、贷款发放后服务费和其他与贷款发放量增加相关的服务费增加。", 209 | "检方并未起诉全部涉嫌贿赂的家长,但起诉名单已有超过50人,耶鲁大学、斯坦福大学等录取率极低的名校涉案也让该事件受到了几乎全球的关注,该案甚至被称作美国“史上最大招生舞弊案”。", 210 | "俄媒称,目前尚不清楚特朗普这一言论的指向性,因为近几日,伊朗官员们都在表达力图避免与美国发生军事冲突的意愿。5月19日早些时候,伊朗革命卫队司令侯赛因·萨拉米称,伊朗只想追求和平,但并不害怕与美国发生战争。萨拉米称,“我们(伊朗)和他们(美国)之间的区别在于,美国害怕发生战争,缺乏开战的意志。”"] 211 | X_new_data = [preprocess(doc) for doc in news_lastest] 212 | X_new_data 213 | ``` 214 | 215 | ['金融 旗下 产品 有 借条 小微贷 分期 借条 是 金融 的 核心 产品 是 一款 无 抵押 纯线 上 消费信贷 产品 为 用户 提供 即时 到 账 贷款 服务 通俗 可以 理解 为 现金 贷 用户 借款 主要 用于 消费 支出 从 收入 构成 来看 金融 主要 有 贷款 便利 服务费 贷后 管理 服务费 融资 收入 其他 服务收入 等 构成 财报 披露 营收 增长 主要 是 由于 贷款 便利化 服务费 贷款 发放 后 服务费 和 其他 与 贷款 发放量 增加 相关 的 服务费 增加', 216 | '检方 并未 起诉 全部 涉嫌 贿赂 的 家长 但 起诉 名单 已有 超过 人 耶鲁大学 斯坦福大学 等 录取率 极低 的 名校 涉案 也 让 该 事件 受到 了 几乎 全球 的 关注 该案 甚至 被称作 美国 史上 最大 招生 舞弊案', 217 | '俄媒称 目前 尚 不 清楚 特朗普 这一 言论 的 指向性 因为 近几日 伊朗 官员 们 都 在 表达 力图 避免 与 美国 发生 军事冲突 的 意愿 月 日 早些时候 伊朗 革命 卫队 司令 侯赛因 · 萨拉米 称 伊朗 只想 追求 和平 但 并 不 害怕 与 美国 发生 战争 萨拉米 称 我们 伊朗 和 他们 美国 之间 的 区别 在于 美国 害怕 发生 战争 缺乏 开战 的 意志'] 218 | 219 | ```python 220 | X_new_tfidf = tfidf_vectorizer.transform(X_new_data) 221 | ``` 222 | 223 | ```python 224 | predicted = classifier.predict(X_new_tfidf) 225 | predicted 226 | ``` 227 | 228 | array(['_08_Finance', '_20_Education', '_24_Military'], dtype=' 364180 300 390 | > 人民文学出版社 0.003146 0.582671 0.049029 -0.312803 0.522986 0.026432 -0.097115 0.194231 -0.362708 ...... 391 | > ...... 392 | 393 | ```python 394 | embeddings_index = load_pre_trained() 395 | ``` 396 | 397 | Found 364180 word vectors, dimension 300 398 | 399 | ### 3. 使用Keras对语料进行处理 400 | 401 | ```python 402 | from keras.preprocessing.text import Tokenizer 403 | from keras.preprocessing.sequence import pad_sequences 404 | from keras.utils import to_categorical 405 | import numpy as np 406 | 407 | MAX_SEQUENCE_LEN = 1000 # 文档限制长度 408 | MAX_WORDS_NUM = 20000 # 词典的个数 409 | VAL_SPLIT_RATIO = 0.2 # 验证集的比例 410 | 411 | tokenizer = Tokenizer(num_words=MAX_WORDS_NUM) 412 | tokenizer.fit_on_texts(texts) 413 | sequences = tokenizer.texts_to_sequences(texts) 414 | 415 | word_index = tokenizer.word_index 416 | print(len(word_index)) # all token found 417 | # print(word_index.get('新闻')) # get word index 418 | dict_swaped = lambda _dict: {val:key for (key, val) in _dict.items()} 419 | word_dict = dict_swaped(word_index) # swap key-value 420 | data = pad_sequences(sequences, maxlen=MAX_SEQUENCE_LEN) 421 | 422 | labels_categorical = to_categorical(np.asarray(labels)) 423 | print('Shape of data tensor:', data.shape) 424 | print('Shape of label tensor:', labels_categorical.shape) 425 | 426 | indices = np.arange(data.shape[0]) 427 | np.random.shuffle(indices) 428 | data = data[indices] 429 | labels_categorical = labels_categorical[indices] 430 | 431 | # split data by ratio 432 | val_samples_num = int(VAL_SPLIT_RATIO * data.shape[0]) 433 | 434 | x_train = data[:-val_samples_num] 435 | y_train = labels_categorical[:-val_samples_num] 436 | x_val = data[-val_samples_num:] 437 | y_val = labels_categorical[-val_samples_num:] 438 | ``` 439 | 440 | 代码中`word_index`表示发现的所有词,得到的文本序列取的是`word_index`中前面20000个词对应的索引,文本序列集合中的所有词的索引号都在20000之前: 441 | 442 | ```python 443 | len(data[data>=20000]) 444 | 0 445 | ``` 446 | 447 | 我们可以通过生成的词索引序列和对应的索引词典查看原始文本和对应的标签: 448 | 449 | ```python 450 | # convert from index to origianl doc 451 | for w_index in data[0]: 452 | if w_index != 0: 453 | print(word_dict[w_index], end=' ') 454 | ``` 455 | 456 | 昆虫 大自然 歌手 昆虫 口腔 发出 昆虫 界 著名 腹部 一对 是从 发出 外面 一对 弹性 称作 声 肌 相连 发音 肌 收缩 振动 声音 空间 响亮 传到 5 0 0 米 求婚 听到 发音 部位 发音 声音 两 张开 蚊子 一对 边缘 支撑 两只 每秒 2 5 0 ~ 6 0 0 次 推动 空气 往返 运动 发出 微弱 声 来源 语文 457 | 458 | ```python 459 | category_labels[dict_swaped(labels_index)[argmax(labels_categorical[0])]] 460 | ``` 461 | 462 | '_20_Education' 463 | 464 | 465 | ### 4. 定义词嵌入矩阵 466 | 467 | 下面创建一个词嵌入矩阵,用来作为上述文本集合词典(只取序号在前`MAX_WORDS_NUM`的词,对应了比较常见的词)的词嵌入矩阵,矩阵维度是`(MAX_WORDS_NUM, EMBEDDING_DIM)`。矩阵的每一行`i`代表词典`word_index`中第`i`个词的词向量。这个词嵌入矩阵是预训练词向量的一个子集。我们的新闻语料中很可能有的词不在预训练词向量中,这样的词在这个词向量矩阵中对应的向量元素都设为零。还记得上面用`pad_sequence`补充的`0`元素么,它对应在词嵌入矩阵的向量也都是零。在本例中,20000个词有92.35%在预训练词向量中。 468 | 469 | 470 | ```python 471 | EMBEDDING_DIM = 300 # embedding dimension 472 | embedding_matrix = np.zeros((MAX_WORDS_NUM+1, EMBEDDING_DIM)) # row 0 for 0 473 | for word, i in word_index.items(): 474 | embedding_vector = embeddings_index.get(word) 475 | if i < MAX_WORDS_NUM: 476 | if embedding_vector is not None: 477 | # Words not found in embedding index will be all-zeros. 478 | embedding_matrix[i] = embedding_vector 479 | ``` 480 | 481 | ```python 482 | nonzero_elements = np.count_nonzero(np.count_nonzero(embedding_matrix, axis=1)) 483 | nonzero_elements / MAX_WORDS_NUM 484 | ``` 485 | 486 | 0.9235 487 | 488 | #### Embedding Layer 489 | 490 | 嵌入层的输入数据`sequence`向量的整数是文本中词的编码,前面看到这个获取序列编码的步骤使用了Keras的`Tokenizer API`来实现,如果不使用预训练词向量模型,嵌入层是用随机权重进行初始化,在训练中将学习到训练集中的所有词的权重,也就是词向量。在定义`Embedding`层,需要至少3个输入数据: 491 | 492 | - `input_dim`:文本词典的大小,本例中就是`MAX_WORDS_NUM + 1`; 493 | - `output_dim`:词嵌入空间的维度,就是词向量的长度,本例中对应`EMBEDDING_DIM`; 494 | - `input_length`:这是输入序列的长度,本例中对应`MAX_SEQUENCE_LEN`。 495 | 496 | 本文中还多了两个输入参数`weights=[embedding_matrix]`和`trainable=False`,前者设置该层的嵌入矩阵为上面我们定义好的词嵌入矩阵,即不适用随机初始化的权重,后者设置为本层参数不可训练,即不会随着后面模型的训练而更改。这里涉及了`Embedding`层的几种使用方式: 497 | 498 | - 从头开始训练出一个词向量,保存之后可以用在其他的训练任务中; 499 | - 嵌入层作为深度学习的第一个隐藏层,本身就是深度学习模型训练的一部分; 500 | - 加载预训练词向量模型,这是一种迁移学习,本文就是这样的示例。 501 | 502 | ### 5. 构建模型 503 | 504 | Keras支持两种类型的模型结构: 505 | 506 | - Sequential类,顺序模型,这个仅用于层的线性堆叠,最常见的网络架构 507 | - Functional API,函数式API,用于层组成的有向无环图,可以构建任意形式的架构 508 | 509 | 为了有个对比,我们先不加载预训练模型,让模型自己训练词权重向量。`Flatten`层用来将输入“压平”,即把多维的输入一维化,这是嵌入层的输出转入全连接层(`Dense`)的必需的过渡。 510 | 511 | 512 | ```python 513 | from keras.models import Sequential 514 | from keras.layers import Dense, Flatten 515 | 516 | input_dim = x_train.shape[1] 517 | 518 | model1 = Sequential() 519 | model1.add(Embedding(input_dim=MAX_WORDS_NUM+1, 520 | output_dim=EMBEDDING_DIM, 521 | input_length=MAX_SEQUENCE_LEN)) 522 | model1.add(Flatten()) 523 | model1.add(Dense(64, activation='relu', input_shape=(input_dim,))) 524 | model1.add(Dense(64, activation='relu')) 525 | model1.add(Dense(len(labels_index), activation='softmax')) 526 | 527 | model1.compile(optimizer='rmsprop', 528 | loss='categorical_crossentropy', 529 | metrics=['accuracy']) 530 | 531 | history1 = model1.fit(x_train, 532 | y_train, 533 | epochs=30, 534 | batch_size=128, 535 | validation_data=(x_val, y_val)) 536 | ``` 537 | 538 | Train on 14328 samples, validate on 3582 samples 539 | Epoch 1/30 540 | 14328/14328 [==============================] - 59s 4ms/step - loss: 3.1273 - acc: 0.2057 - val_loss: 1.9355 - val_acc: 0.2510 541 | Epoch 2/30 542 | 14328/14328 [==============================] - 56s 4ms/step - loss: 2.0853 - acc: 0.3349 - val_loss: 1.8037 - val_acc: 0.3473 543 | Epoch 3/30 544 | 14328/14328 [==============================] - 56s 4ms/step - loss: 1.7210 - acc: 0.4135 - val_loss: 1.2498 - val_acc: 0.5731 545 | ...... 546 | Epoch 29/30 547 | 14328/14328 [==============================] - 56s 4ms/step - loss: 0.5843 - acc: 0.8566 - val_loss: 1.3564 - val_acc: 0.6516 548 | Epoch 30/30 549 | 14328/14328 [==============================] - 56s 4ms/step - loss: 0.5864 - acc: 0.8575 - val_loss: 0.5970 - val_acc: 0.8501 550 | 551 | 552 | 每个Keras层都提供了获取或设置本层权重参数的方法: 553 | 554 | - `layer.get_weights()`:返回层的权重(`numpy array`) 555 | - `layer.set_weights(weights)`:从`numpy array`中将权重加载到该层中,要求`numpy array`的形状与`layer.get_weights()`的形状相同 556 | 557 | 558 | ```python 559 | embedding_custom = model1.layers[0].get_weights()[0] 560 | embedding_custom 561 | ``` 562 | 563 | array([[ 0.39893672, -0.9062594 , 0.35500282, ..., -0.73564297, 564 | 0.50492775, -0.39815223], 565 | [ 0.10640696, 0.18888871, 0.05909824, ..., -0.1642032 , 566 | -0.02778293, -0.15340094], 567 | [ 0.06566656, -0.04023357, 0.1276007 , ..., 0.04459211, 568 | 0.08887506, 0.05389333], 569 | ..., 570 | [-0.12710813, -0.08472785, -0.2296919 , ..., 0.0468552 , 571 | 0.12868881, 0.18596107], 572 | [-0.03790742, 0.09758633, 0.02123675, ..., -0.08180046, 573 | 0.10254312, 0.01284804], 574 | [-0.0100647 , 0.01180602, 0.00446023, ..., 0.04730382, 575 | -0.03696882, 0.00119566]], dtype=float32) 576 | 577 | 578 | 579 | `get_weights`方法得到的就是词嵌入矩阵,如果本例中取的词典足够大,这样的词嵌入矩阵就可以保存下来,作为其他任务的预训练模型使用。通过`get_config()`可以获取每一层的配置信息: 580 | 581 | 582 | ```python 583 | model1.layers[0].get_config() 584 | ``` 585 | 586 | {'activity_regularizer': None, 587 | 'batch_input_shape': (None, 1000), 588 | 'dtype': 'float32', 589 | 'embeddings_constraint': None, 590 | 'embeddings_initializer': {'class_name': 'RandomUniform', 591 | 'config': {'maxval': 0.05, 'minval': -0.05, 'seed': None}}, 592 | 'embeddings_regularizer': None, 593 | 'input_dim': 20001, 594 | 'input_length': 1000, 595 | 'mask_zero': False, 596 | 'name': 'embedding_13', 597 | 'output_dim': 300, 598 | 'trainable': True} 599 | 600 | 可以将模型训练的结果打印出来 601 | 602 | ```python 603 | plot_history(history1) 604 | ``` 605 | 606 | ![acc_loss_model1](img/acc_loss_model1.png) 607 | 608 | 第一个模型训练时间花了大约30分钟训练完30个epoch,这是因为模型需要训练嵌入层的参数,下面第二个模型在第一个模型基础上加载词嵌入矩阵,并将词嵌入矩阵设为不可训练,看是否可以提高训练的效率。 609 | 610 | 611 | ```python 612 | from keras.models import Sequential 613 | from keras.layers import Dense, Flatten 614 | 615 | input_dim = x_train.shape[1] 616 | 617 | model2 = Sequential() 618 | model2.add(Embedding(input_dim=MAX_WORDS_NUM+1, 619 | output_dim=EMBEDDING_DIM, 620 | weights=[embedding_matrix], 621 | input_length=MAX_SEQUENCE_LEN, 622 | trainable=False)) 623 | model2.add(Flatten()) 624 | model2.add(Dense(64, activation='relu', input_shape=(input_dim,))) 625 | model2.add(Dense(64, activation='relu')) 626 | model2.add(Dense(len(labels_index), activation='softmax')) 627 | 628 | model2.compile(optimizer='rmsprop', 629 | loss='categorical_crossentropy', 630 | metrics=['accuracy']) 631 | 632 | history2 = model2.fit(x_train, 633 | y_train, 634 | epochs=10, 635 | batch_size=128, 636 | validation_data=(x_val, y_val)) 637 | ``` 638 | 639 | Train on 14328 samples, validate on 3582 samples 640 | Epoch 1/10 641 | 14328/14328 [==============================] - 37s 3ms/step - loss: 1.3124 - acc: 0.6989 - val_loss: 0.7446 - val_acc: 0.8088 642 | Epoch 2/10 643 | 14328/14328 [==============================] - 35s 2ms/step - loss: 0.2831 - acc: 0.9243 - val_loss: 0.5712 - val_acc: 0.8551 644 | Epoch 3/10 645 | 14328/14328 [==============================] - 35s 2ms/step - loss: 0.1183 - acc: 0.9704 - val_loss: 0.6261 - val_acc: 0.8624 646 | Epoch 4/10 647 | 14328/14328 [==============================] - 35s 2ms/step - loss: 0.0664 - acc: 0.9801 - val_loss: 0.6897 - val_acc: 0.8607 648 | Epoch 5/10 649 | 14328/14328 [==============================] - 35s 2ms/step - loss: 0.0549 - acc: 0.9824 - val_loss: 0.7199 - val_acc: 0.8660 650 | Epoch 6/10 651 | 14328/14328 [==============================] - 35s 2ms/step - loss: 0.0508 - acc: 0.9849 - val_loss: 0.7261 - val_acc: 0.8582 652 | Epoch 7/10 653 | 14328/14328 [==============================] - 35s 2ms/step - loss: 0.0513 - acc: 0.9865 - val_loss: 0.8251 - val_acc: 0.8585 654 | Epoch 8/10 655 | 14328/14328 [==============================] - 35s 2ms/step - loss: 0.0452 - acc: 0.9858 - val_loss: 0.7891 - val_acc: 0.8707 656 | Epoch 9/10 657 | 14328/14328 [==============================] - 35s 2ms/step - loss: 0.0469 - acc: 0.9865 - val_loss: 0.8663 - val_acc: 0.8680 658 | Epoch 10/10 659 | 14328/14328 [==============================] - 35s 2ms/step - loss: 0.0418 - acc: 0.9867 - val_loss: 0.9048 - val_acc: 0.8640 660 | 661 | 662 | 663 | ```python 664 | plot_history(history2) 665 | ``` 666 | 667 | ![acc_loss_model2](img/acc_loss_model2.png) 668 | 669 | 670 | 从第二个模型训练结果可以看到预训练模型的加载可以大幅提高模型训练的效率,模型的验证准确度也提升的比较快,但是同时发现在训练集上出现了过拟合的情况。 671 | 672 | 第三个模型的结构来自于Keras作者的博客[示例](https://blog.keras.io/using-pre-trained-word-embeddings-in-a-keras-model.html),这是CNN用于文本分类的例子。 673 | 674 | ```python 675 | from keras.layers import Dense, Input, Embedding 676 | from keras.layers import Conv1D, MaxPooling1D, Flatten 677 | from keras.models import Model 678 | 679 | embedding_layer = Embedding(input_dim=MAX_WORDS_NUM+1, 680 | output_dim=EMBEDDING_DIM, 681 | weights=[embedding_matrix], 682 | input_length=MAX_SEQUENCE_LEN, 683 | trainable=False) 684 | 685 | 686 | sequence_input = Input(shape=(MAX_SEQUENCE_LEN,), dtype='int32') 687 | embedded_sequences = embedding_layer(sequence_input) 688 | x = Conv1D(128, 5, activation='relu')(embedded_sequences) 689 | x = MaxPooling1D(5)(x) 690 | x = Conv1D(128, 5, activation='relu')(x) 691 | x = MaxPooling1D(5)(x) 692 | x = Conv1D(128, 5, activation='relu')(x) 693 | x = MaxPooling1D(35)(x) # global max pooling 694 | x = Flatten()(x) 695 | x = Dense(128, activation='relu')(x) 696 | preds = Dense(len(labels_index), activation='softmax')(x) 697 | 698 | model3 = Model(sequence_input, preds) 699 | model3.compile(loss='categorical_crossentropy', 700 | optimizer='rmsprop', 701 | metrics=['acc']) 702 | 703 | history3 = model3.fit(x_train, 704 | y_train, 705 | epochs=6, 706 | batch_size=128, 707 | validation_data=(x_val, y_val)) 708 | ``` 709 | 710 | Train on 14328 samples, validate on 3582 samples 711 | Epoch 1/6 712 | 14328/14328 [==============================] - 77s 5ms/step - loss: 0.9943 - acc: 0.6719 - val_loss: 0.5129 - val_acc: 0.8582 713 | Epoch 2/6 714 | 14328/14328 [==============================] - 76s 5ms/step - loss: 0.4841 - acc: 0.8571 - val_loss: 0.3929 - val_acc: 0.8841 715 | Epoch 3/6 716 | 14328/14328 [==============================] - 77s 5ms/step - loss: 0.3483 - acc: 0.8917 - val_loss: 0.4022 - val_acc: 0.8724 717 | Epoch 4/6 718 | 14328/14328 [==============================] - 77s 5ms/step - loss: 0.2763 - acc: 0.9100 - val_loss: 0.3441 - val_acc: 0.8942 719 | Epoch 5/6 720 | 14328/14328 [==============================] - 76s 5ms/step - loss: 0.2194 - acc: 0.9259 - val_loss: 0.3014 - val_acc: 0.9107 721 | Epoch 6/6 722 | 14328/14328 [==============================] - 77s 5ms/step - loss: 0.1749 - acc: 0.9387 - val_loss: 0.3895 - val_acc: 0.8788 723 | 724 | 725 | 726 | ```python 727 | plot_history(history3) 728 | ``` 729 | 730 | 731 | ![acc_loss_model3_cnn](img/acc_loss_model3_cnn.png) 732 | 733 | 734 | 通过加入池化层`MaxPooling1D`,降低了过拟合的情况。验证集上的准备度超过了前两个模型,也超过了传统机器学习方法。 735 | 736 | 737 | ### 参考资料 738 | 739 | - [Deep Learning, NLP, and Representations](http://colah.github.io/posts/2014-07-NLP-RNNs-Representations/) 740 | - [Keras Embedding Layers API](https://keras.io/layers/embeddings/) 741 | - [How to Use Word Embedding Layers for Deep Learning with Keras](https://machinelearningmastery.com/use-word-embedding-layers-deep-learning-keras/) 742 | - [Practical Text Classification With Python and Keras](https://realpython.com/python-keras-text-classification/) 743 | - [Francois Chollet: Using pre-trained word embeddings in a Keras model](https://blog.keras.io/using-pre-trained-word-embeddings-in-a-keras-model.html) 744 | --------------------------------------------------------------------------------