基于循環(huán)神經(jīng)網(wǎng)絡(luò)(RNN)實(shí)現(xiàn)影評(píng)情感分類
使用循環(huán)神經(jīng)網(wǎng)絡(luò)(RNN)實(shí)現(xiàn)影評(píng)情感分類
作為對(duì)循環(huán)神經(jīng)網(wǎng)絡(luò)的實(shí)踐,我用循環(huán)神經(jīng)網(wǎng)絡(luò)做了個(gè)影評(píng)情感的分類,即判斷影評(píng)的感情色彩是正面的,還是負(fù)面的。
選擇使用RNN來(lái)做情感分類,主要是因?yàn)橛霸u(píng)是一段文字,是序列的,而RNN對(duì)序列的支持比較好,能夠“記憶”前文。雖然可以提取特征詞向量,然后交給傳統(tǒng)機(jī)器學(xué)習(xí)模型或全連接神經(jīng)網(wǎng)絡(luò)去做,也能取得很好的效果,但只從端對(duì)端的角度來(lái)看的話,RNN無(wú)疑是最合適的。
以下介紹實(shí)現(xiàn)過程。
一、數(shù)據(jù)預(yù)處理
本文中使用的訓(xùn)練數(shù)據(jù)集為https://www.cs.cornell.edu/people/pabo/movie-review-data/上的sentence polarity dataset v1.0,包含正負(fù)面評(píng)論各5331條??梢渣c(diǎn)擊進(jìn)行下載。
數(shù)據(jù)下載下來(lái)之后需要進(jìn)行解壓,得到rt-polarity.neg和rt-polarity.pos文件,這兩個(gè)文件是Windows-1252編碼的,先將它轉(zhuǎn)成unicode處理起來(lái)會(huì)更方便。
補(bǔ)充一下小知識(shí),當(dāng)我們打開一個(gè)文件,發(fā)現(xiàn)亂碼,卻又不知道該文件的編碼是什么的時(shí)候,可以使用python的chardet類庫(kù)進(jìn)行判斷,這里的Windows-1252就是使用該類庫(kù)檢測(cè)出來(lái)的。
在數(shù)據(jù)預(yù)處理部分,我們要完成如下處理過程:
1.轉(zhuǎn)碼
即將文件轉(zhuǎn)為unicode編碼,方便我們后續(xù)操作。讀取文件,轉(zhuǎn)換編碼,重新寫入到新文件即可。不存在技術(shù)難點(diǎn)。
2.生成詞匯表
讀取訓(xùn)練文件,提取出所有的單詞,并統(tǒng)計(jì)各個(gè)單詞出現(xiàn)的次數(shù)。為了避免低頻詞的干擾,同時(shí)減少模型參數(shù),我們只保留部分高頻詞,比如這里我只保存出現(xiàn)次數(shù)前9999個(gè),同時(shí)將低頻詞標(biāo)識(shí)符<unkown>加入到詞匯表中。
3.借助詞匯表將影評(píng)轉(zhuǎn)化為詞向量
單詞是沒法直接輸入給模型的,所以我們需要將詞匯表中的每個(gè)單詞對(duì)應(yīng)于一個(gè)編號(hào),將影評(píng)數(shù)據(jù)轉(zhuǎn)化成詞向量。方便后面生成詞嵌入矩陣。
4.填充詞向量并轉(zhuǎn)化為np數(shù)組
因?yàn)椴煌u(píng)論的長(zhǎng)度是不同的,我們要組成batch進(jìn)行訓(xùn)練,就需要先將其長(zhǎng)度統(tǒng)一。這里我選擇以最長(zhǎng)的影評(píng)為標(biāo)準(zhǔn),對(duì)其他較短的影評(píng)的空白部分進(jìn)行填充。然后將其轉(zhuǎn)化成numpy的數(shù)組。
5.按比例劃分?jǐn)?shù)據(jù)集
按照機(jī)器學(xué)習(xí)的慣例,數(shù)據(jù)集應(yīng)被劃分為三份,即訓(xùn)練集、開發(fā)集和測(cè)試集。當(dāng)然,有時(shí)也會(huì)只劃分兩份,即只包括訓(xùn)練集和開發(fā)集。
這里我劃分成三份,訓(xùn)練集、開發(fā)集和測(cè)試集的占比為[0.8,0.1,0.1]。劃分的方式為輪盤賭法,在numpy中可以使用cumsum和searchsorted來(lái)簡(jiǎn)潔地實(shí)現(xiàn)輪盤賭法。
6.打亂數(shù)據(jù)集,寫入文件
為了取得更好的訓(xùn)練效果,將數(shù)據(jù)集隨機(jī)打亂。為了保證在訓(xùn)練和模型調(diào)整的過程中訓(xùn)練集、開發(fā)集、測(cè)試集不發(fā)生改變,將三個(gè)數(shù)據(jù)集寫入到文件中,使用的時(shí)候從文件中讀取。
下面貼上數(shù)據(jù)預(yù)處理的代碼,注釋寫的很細(xì),就不多說了。
# -*- coding: utf-8 -*- # @Time : 18-3-14 下午2:28 # @Author : AaronJny # @Email : Aaron__7@163.com import sys reload(sys) sys.setdefaultencoding('utf8') import collections import settings import utils import numpy as np def create_vocab(): """ 創(chuàng)建詞匯表,寫入文件中 :return: """ # 存放出現(xiàn)的所有單詞 word_list = [] # 從文件中讀取數(shù)據(jù),拆分單詞 with open(settings.NEG_TXT, 'r') as f: f_lines = f.readlines() for line in f_lines: words = line.strip().split() word_list.extend(words) with open(settings.POS_TXT, 'r') as f: f_lines = f.readlines() for line in f_lines: words = line.strip().split() word_list.extend(words) # 統(tǒng)計(jì)單詞出現(xiàn)的次數(shù) counter = collections.Counter(word_list) sorted_words = sorted(counter.items(), key=lambda x: x[1], reverse=True) # 選取高頻詞 word_list = [word[0] for word in sorted_words] word_list = ['<unkown>'] + word_list[:settings.VOCAB_SIZE - 1] # 將詞匯表寫入文件中 with open(settings.VOCAB_PATH, 'w') as f: for word in word_list: f.write(word + '\n') def create_vec(txt_path, vec_path): """ 根據(jù)詞匯表生成詞向量 :param txt_path: 影評(píng)文件路徑 :param vec_path: 輸出詞向量路徑 :return: """ # 獲取單詞到編號(hào)的映射 word2id = utils.read_word_to_id_dict() # 將語(yǔ)句轉(zhuǎn)化成向量 vec = [] with open(txt_path, 'r') as f: f_lines = f.readlines() for line in f_lines: tmp_vec = [str(utils.get_id_by_word(word, word2id)) for word in line.strip().split()] vec.append(tmp_vec) # 寫入文件中 with open(vec_path, 'w') as f: for tmp_vec in vec: f.write(' '.join(tmp_vec) + '\n') def cut_train_dev_test(): """ 使用輪盤賭法,劃分訓(xùn)練集、開發(fā)集和測(cè)試集 打亂,并寫入不同文件中 :return: """ # 三個(gè)位置分別存放訓(xùn)練、開發(fā)、測(cè)試 data = [[], [], []] labels = [[], [], []] # 累加概率 rate [0.8,0.1,0.1] cumsum_rate [0.8,0.9,1.0] rate = np.array([settings.TRAIN_RATE, settings.DEV_RATE, settings.TEST_RATE]) cumsum_rate = np.cumsum(rate) # 使用輪盤賭法劃分?jǐn)?shù)據(jù)集 with open(settings.POS_VEC, 'r') as f: f_lines = f.readlines() for line in f_lines: tmp_data = [int(word) for word in line.strip().split()] tmp_label = [1, ] index = int(np.searchsorted(cumsum_rate, np.random.rand(1) * 1.0)) data[index].append(tmp_data) labels[index].append(tmp_label) with open(settings.NEG_VEC, 'r') as f: f_lines = f.readlines() for line in f_lines: tmp_data = [int(word) for word in line.strip().split()] tmp_label = [0, ] index = int(np.searchsorted(cumsum_rate, np.random.rand(1) * 1.0)) data[index].append(tmp_data) labels[index].append(tmp_label) # 計(jì)算一下實(shí)際上分割出來(lái)的比例 print '最終分割比例', np.array([map(len, data)], dtype=np.float32) / sum(map(len, data)) # 打亂數(shù)據(jù),寫入到文件中 shuffle_data(data[0], labels[0], settings.TRAIN_DATA) shuffle_data(data[1], labels[1], settings.DEV_DATA) shuffle_data(data[2], labels[2], settings.TEST_DATA) def shuffle_data(x, y, path): """ 填充數(shù)據(jù),生成np數(shù)組 打亂數(shù)據(jù),寫入文件中 :param x: 數(shù)據(jù) :param y: 標(biāo)簽 :param path: 保存路徑 :return: """ # 計(jì)算影評(píng)的最大長(zhǎng)度 maxlen = max(map(len, x)) # 填充數(shù)據(jù) data = np.zeros([len(x), maxlen], dtype=np.int32) for row in range(len(x)): data[row, :len(x[row])] = x[row] label = np.array(y) # 打亂數(shù)據(jù) state = np.random.get_state() np.random.shuffle(data) np.random.set_state(state) np.random.shuffle(label) # 保存數(shù)據(jù) np.save(path + '_data', data) np.save(path + '_labels', label) def decode_file(infile, outfile): """ 將文件的編碼從'Windows-1252'轉(zhuǎn)為Unicode :param infile: 輸入文件路徑 :param outfile: 輸出文件路徑 :return: """ with open(infile, 'r') as f: txt = f.read().decode('Windows-1252') with open(outfile, 'w') as f: f.write(txt) if __name__ == '__main__': # 解碼文件 decode_file(settings.ORIGIN_POS, settings.POS_TXT) decode_file(settings.ORIGIN_NEG, settings.NEG_TXT) # 創(chuàng)建詞匯表 create_vocab() # 生成詞向量 create_vec(settings.NEG_TXT, settings.NEG_VEC) create_vec(settings.POS_TXT, settings.POS_VEC) # 劃分?jǐn)?shù)據(jù)集 cut_train_dev_test()
二、模型編寫
數(shù)據(jù)處理好之后,開始模型的編寫。這里選用循環(huán)神經(jīng)網(wǎng)絡(luò),建模過程大致如下:
1.使用embedding構(gòu)建詞嵌入矩陣
在數(shù)據(jù)預(yù)處理中,我們將影評(píng)處理成了一個(gè)個(gè)單詞編號(hào)構(gòu)成的向量,也就是說,一條影評(píng),對(duì)應(yīng)于一個(gè)由單詞編號(hào)構(gòu)成的向量。
將這樣的向量進(jìn)行embedding,即可構(gòu)建出詞嵌入矩陣。在詞嵌入矩陣中,每個(gè)詞由一個(gè)向量表示,矩陣中不同向量之間的差異對(duì)應(yīng)于它們表示的詞之間的差異。
2.使用LSTM作為循環(huán)神經(jīng)網(wǎng)絡(luò)的基本單元
長(zhǎng)短時(shí)記憶網(wǎng)絡(luò)(LSTM)能夠自動(dòng)完成前文信息的“記憶”和“遺忘”,在循環(huán)神經(jīng)網(wǎng)絡(luò)中表現(xiàn)良好,已經(jīng)成為在循環(huán)神經(jīng)網(wǎng)絡(luò)中大部分人的首選。這里我選擇使用LSTM作為循環(huán)神經(jīng)網(wǎng)絡(luò)的基本單元。
3.對(duì)embedding和LSTM進(jìn)行隨機(jī)失活(dropout)
為了提高模型的泛化能力,并減少參數(shù),我對(duì)embedding層和LSTM單元進(jìn)行dropout。
4.建立深度為2的深度循環(huán)神經(jīng)網(wǎng)絡(luò)
為了提高模型的擬合能力,使用深度循環(huán)神經(jīng)網(wǎng)絡(luò),我選擇的深度為2。
5.給出二分類概率
對(duì)深度循環(huán)神經(jīng)網(wǎng)絡(luò)的最后節(jié)點(diǎn)的輸出做邏輯回歸,通過sigmoid使結(jié)果落到0-1之間,代表結(jié)果是正類的概率。
損失函數(shù)使用交叉熵,優(yōu)化器選擇Adam。
此部分代碼如下(注:代碼中裝飾器的作用為劃分命名空間以及保證張量運(yùn)算只被定義一次):
# -*- coding: utf-8 -*- # @Time : 18-3-14 下午2:57 # @Author : AaronJny # @Email : Aaron__7@163.com import tensorflow as tf import functools import settings HIDDEN_SIZE = 128 NUM_LAYERS = 2 def doublewrap(function): @functools.wraps(function) def decorator(*args, **kwargs): if len(args) == 1 and len(kwargs) == 0 and callable(args[0]): return function(args[0]) else: return lambda wrapee: function(wrapee, *args, **kwargs) return decorator @doublewrap def define_scope(function, scope=None, *args, **kwargs): attribute = '_cache_' + function.__name__ name = scope or function.__name__ @property @functools.wraps(function) def decorator(self): if not hasattr(self, attribute): with tf.variable_scope(name, *args, **kwargs): setattr(self, attribute, function(self)) return getattr(self, attribute) return decorator class Model(object): def __init__(self, data, lables, emb_keep, rnn_keep): """ 神經(jīng)網(wǎng)絡(luò)模型 :param data:數(shù)據(jù) :param lables: 標(biāo)簽 :param emb_keep: emb層保留率 :param rnn_keep: rnn層保留率 """ self.data = data self.label = lables self.emb_keep = emb_keep self.rnn_keep = rnn_keep self.predict self.loss self.global_step self.ema self.optimize self.acc @define_scope def predict(self): """ 定義前向傳播過程 :return: """ # 詞嵌入矩陣權(quán)重 embedding = tf.get_variable('embedding', [settings.VOCAB_SIZE, HIDDEN_SIZE]) # 使用dropout的LSTM lstm_cell = [tf.nn.rnn_cell.DropoutWrapper(tf.nn.rnn_cell.BasicLSTMCell(HIDDEN_SIZE), self.rnn_keep) for _ in range(NUM_LAYERS)] # 構(gòu)建循環(huán)神經(jīng)網(wǎng)絡(luò) cell = tf.nn.rnn_cell.MultiRNNCell(lstm_cell) # 生成詞嵌入矩陣,并進(jìn)行dropout input = tf.nn.embedding_lookup(embedding, self.data) dropout_input = tf.nn.dropout(input, self.emb_keep) # 計(jì)算rnn的輸出 outputs, last_state = tf.nn.dynamic_rnn(cell, dropout_input, dtype=tf.float32) # 做二分類問題,這里只需要最后一個(gè)節(jié)點(diǎn)的輸出 last_output = outputs[:, -1, :] # 求最后節(jié)點(diǎn)輸出的線性加權(quán)和 weights = tf.Variable(tf.truncated_normal([HIDDEN_SIZE, 1]), dtype=tf.float32, name='weights') bias = tf.Variable(0, dtype=tf.float32, name='bias') logits = tf.matmul(last_output, weights) + bias return logits @define_scope def ema(self): """ 定義移動(dòng)平均 :return: """ ema = tf.train.ExponentialMovingAverage(settings.EMA_RATE, self.global_step) return ema @define_scope def loss(self): """ 定義損失函數(shù),這里使用交叉熵 :return: """ loss = tf.nn.sigmoid_cross_entropy_with_logits(labels=self.label, logits=self.predict) loss = tf.reduce_mean(loss) return loss @define_scope def global_step(self): """ step,沒什么好說的,注意指定trainable=False :return: """ global_step = tf.Variable(0, trainable=False) return global_step @define_scope def optimize(self): """ 定義反向傳播過程 :return: """ # 學(xué)習(xí)率衰減 learn_rate = tf.train.exponential_decay(settings.LEARN_RATE, self.global_step, settings.LR_DECAY_STEP, settings.LR_DECAY) # 反向傳播優(yōu)化器 optimizer = tf.train.AdamOptimizer(learn_rate).minimize(self.loss, global_step=self.global_step) # 移動(dòng)平均操作 ave_op = self.ema.apply(tf.trainable_variables()) # 組合構(gòu)成訓(xùn)練op with tf.control_dependencies([optimizer, ave_op]): train_op = tf.no_op('train') return train_op @define_scope def acc(self): """ 定義模型acc計(jì)算過程 :return: """ # 對(duì)前向傳播的結(jié)果求sigmoid output = tf.nn.sigmoid(self.predict) # 真負(fù)類 ok0 = tf.logical_and(tf.less_equal(output, 0.5), tf.equal(self.label, 0)) # 真正類 ok1 = tf.logical_and(tf.greater(output, 0.5), tf.equal(self.label, 1)) # 一個(gè)數(shù)組,所有預(yù)測(cè)正確的都為True,否則False ok = tf.logical_or(ok0, ok1) # 先轉(zhuǎn)化成浮點(diǎn)型,再通過求平均來(lái)計(jì)算acc acc = tf.reduce_mean(tf.cast(ok, dtype=tf.float32)) return acc
三、組織數(shù)據(jù)集
我編寫了一個(gè)類用于組織數(shù)據(jù),方便訓(xùn)練和驗(yàn)證使用。代碼很簡(jiǎn)單,就不多說了,直接貼代碼:
# -*- coding: utf-8 -*- # @Time : 18-3-14 下午3:33 # @Author : AaronJny # @Email : Aaron__7@163.com import numpy as np import settings class Dataset(object): def __init__(self, data_kind=0): """ 生成一個(gè)數(shù)據(jù)集對(duì)象 :param data_kind: 決定了使用哪種數(shù)據(jù)集 0-訓(xùn)練集 1-開發(fā)集 2-測(cè)試集 """ self.data, self.labels = self.read_data(data_kind) self.start = 0 # 記錄當(dāng)前batch位置 self.data_size = len(self.data) # 樣例數(shù) def read_data(self, data_kind): """ 從文件中加載數(shù)據(jù) :param data_kind:數(shù)據(jù)集種類 0-訓(xùn)練集 1-開發(fā)集 2-測(cè)試集 :return: """ # 獲取數(shù)據(jù)集路徑 data_path = [settings.TRAIN_DATA, settings.DEV_DATA, settings.TEST_DATA][data_kind] # 加載 data = np.load(data_path + '_data.npy') labels = np.load(data_path + '_labels.npy') return data, labels def next_batch(self, batch_size): """ 獲取一個(gè)大小為batch_size的batch :param batch_size: batch大小 :return: """ start = self.start end = min(start + batch_size, self.data_size) self.start = end # 當(dāng)遍歷完成后回到起點(diǎn) if self.start >= self.data_size: self.start = 0 # 返回一個(gè)batch的數(shù)據(jù)和標(biāo)簽 return self.data[start:end], self.labels[start:end]
四、模型訓(xùn)練
訓(xùn)練過程中,額外操作主要有兩個(gè):
1.使用移動(dòng)平均
我使用移動(dòng)平均的主要目的是使loss曲線盡量平滑,以及提升模型的泛化能力。
2.使用學(xué)習(xí)率指數(shù)衰減
目的是保證前期學(xué)習(xí)率足夠大,能夠快速降低loss,后期學(xué)習(xí)率變小,能更好地逼近最優(yōu)解。
當(dāng)然,就是說說而已,這次的訓(xùn)練數(shù)據(jù)比較簡(jiǎn)單,學(xué)習(xí)率衰減發(fā)揮的作用不大。
訓(xùn)練過程中,定期保存模型,以及checkpoint。這樣可以在訓(xùn)練的同時(shí),在驗(yàn)證腳本中讀取最新模型進(jìn)行驗(yàn)證。
此部分具體代碼如下:
# -*- coding: utf-8 -*- # @Time : 18-3-14 下午4:41 # @Author : AaronJny # @Email : Aaron__7@163.com import settings import tensorflow as tf import models import dataset import os BATCH_SIZE = settings.BATCH_SIZE # 數(shù)據(jù) x = tf.placeholder(tf.int32, [None, None]) # 標(biāo)簽 y = tf.placeholder(tf.float32, [None, 1]) # emb層的dropout保留率 emb_keep = tf.placeholder(tf.float32) # rnn層的dropout保留率 rnn_keep = tf.placeholder(tf.float32) # 創(chuàng)建一個(gè)模型 model = models.Model(x, y, emb_keep, rnn_keep) # 創(chuàng)建數(shù)據(jù)集對(duì)象 data = dataset.Dataset(0) saver = tf.train.Saver() with tf.Session() as sess: # 全局初始化 sess.run(tf.global_variables_initializer()) # 迭代訓(xùn)練 for step in range(settings.TRAIN_TIMES): # 獲取一個(gè)batch進(jìn)行訓(xùn)練 x, y = data.next_batch(BATCH_SIZE) loss, _ = sess.run([model.loss, model.optimize], {model.data: x, model.label: y, model.emb_keep: settings.EMB_KEEP_PROB, model.rnn_keep: settings.RNN_KEEP_PROB}) # 輸出loss if step % settings.SHOW_STEP == 0: print 'step {},loss is {}'.format(step, loss) # 保存模型 if step % settings.SAVE_STEP == 0: saver.save(sess, os.path.join(settings.CKPT_PATH, settings.MODEL_NAME), model.global_step)
五、驗(yàn)證模型
加載最新模型進(jìn)行驗(yàn)證,通過修改數(shù)據(jù)集對(duì)象的參數(shù)可以制定訓(xùn)練/開發(fā)/測(cè)試集進(jìn)行驗(yàn)證。
加載模型的時(shí)候,使用移動(dòng)平均的影子變量覆蓋對(duì)應(yīng)變量。
代碼如下:
# -*- coding: utf-8 -*- # @Time : 18-3-14 下午5:09 # @Author : AaronJny # @Email : Aaron__7@163.com import settings import tensorflow as tf import models import dataset import os import time # 為了在使用GPU訓(xùn)練的同時(shí),使用CPU進(jìn)行驗(yàn)證 os.environ['CUDA_VISIBLE_DEVICES'] = '' BATCH_SIZE = settings.BATCH_SIZE # 數(shù)據(jù) x = tf.placeholder(tf.int32, [None, None]) # 標(biāo)簽 y = tf.placeholder(tf.float32, [None, 1]) # emb層的dropout保留率 emb_keep = tf.placeholder(tf.float32) # rnn層的dropout保留率 rnn_keep = tf.placeholder(tf.float32) # 創(chuàng)建一個(gè)模型 model = models.Model(x, y, emb_keep, rnn_keep) # 創(chuàng)建一個(gè)數(shù)據(jù)集對(duì)象 data = dataset.Dataset(1) # 0-訓(xùn)練集 1-開發(fā)集 2-測(cè)試集 # 移動(dòng)平均變量 restore_variables = model.ema.variables_to_restore() # 使用移動(dòng)平均變量進(jìn)行覆蓋 saver = tf.train.Saver(restore_variables) with tf.Session() as sess: while True: # 加載最新的模型 ckpt = tf.train.get_checkpoint_state(settings.CKPT_PATH) saver.restore(sess, ckpt.model_checkpoint_path) # 計(jì)算并輸出acc acc = sess.run([model.acc], {model.data: data.data, model.label: data.labels, model.emb_keep: 1.0, model.rnn_keep: 1.0}) print 'acc is ', acc time.sleep(1)
六、對(duì)詞匯表進(jìn)行操作的幾個(gè)方法
把對(duì)詞匯表進(jìn)行操作的幾個(gè)方法提取出來(lái)了,放到了utils.py文件中。
# -*- coding: utf-8 -*- # @Time : 18-3-14 下午2:44 # @Author : AaronJny # @Email : Aaron__7@163.com import settings def read_vocab_list(): """ 讀取詞匯表 :return:由詞匯表中所有單詞組成的列表 """ with open(settings.VOCAB_PATH, 'r') as f: vocab_list = f.read().strip().split('\n') return vocab_list def read_word_to_id_dict(): """ 生成一個(gè)單詞到編號(hào)的映射 :return:單詞到編號(hào)的字典 """ vocab_list = read_vocab_list() word2id = dict(zip(vocab_list, range(len(vocab_list)))) return word2id def read_id_to_word_dict(): """ 生成一個(gè)編號(hào)到單詞的映射 :return:編號(hào)到單詞的字典 """ vocab_list = read_vocab_list() id2word = dict(zip(range(len(vocab_list)), vocab_list)) return id2word def get_id_by_word(word, word2id): """ 給定一個(gè)單詞和字典,獲得單詞在字典中的編號(hào) :param word: 給定單詞 :param word2id: 單詞到編號(hào)的映射 :return: 若單詞在字典中,返回對(duì)應(yīng)的編號(hào) 否則,返回word2id['<unkown>'] """ if word in word2id: return word2id[word] else: return word2id['<unkown>']
七、對(duì)模型進(jìn)行配置
模型的配置參數(shù)大多數(shù)都被提取出來(lái),單獨(dú)放到了settings.py文件中,可以在這里對(duì)模型進(jìn)行配置。
# -*- coding: utf-8 -*- # @Time : 18-3-14 下午2:44 # @Author : AaronJny # @Email : Aaron__7@163.com # 源數(shù)據(jù)路徑 ORIGIN_NEG = 'data/rt-polarity.neg' ORIGIN_POS = 'data/rt-polarity.pos' # 轉(zhuǎn)碼后的數(shù)據(jù)路徑 NEG_TXT = 'data/neg.txt' POS_TXT = 'data/pos.txt' # 詞匯表路徑 VOCAB_PATH = 'data/vocab.txt' # 詞向量路徑 NEG_VEC = 'data/neg.vec' POS_VEC = 'data/pos.vec' # 訓(xùn)練集路徑 TRAIN_DATA = 'data/train' # 開發(fā)集路徑 DEV_DATA = 'data/dev' # 測(cè)試集路徑 TEST_DATA = 'data/test' # 模型保存路徑 CKPT_PATH = 'ckpt' # 模型名稱 MODEL_NAME = 'model' # 詞匯表大小 VOCAB_SIZE = 10000 # 初始學(xué)習(xí)率 LEARN_RATE = 0.0001 # 學(xué)習(xí)率衰減 LR_DECAY = 0.99 # 衰減頻率 LR_DECAY_STEP = 1000 # 總訓(xùn)練次數(shù) TRAIN_TIMES = 2000 # 顯示訓(xùn)練loss的頻率 SHOW_STEP = 10 # 保存訓(xùn)練模型的頻率 SAVE_STEP = 100 # 訓(xùn)練集占比 TRAIN_RATE = 0.8 # 開發(fā)集占比 DEV_RATE = 0.1 # 測(cè)試集占比 TEST_RATE = 0.1 # BATCH大小 BATCH_SIZE = 64 # emb層dropout保留率 EMB_KEEP_PROB = 0.5 # rnn層dropout保留率 RNN_KEEP_PROB = 0.5 # 移動(dòng)平均衰減率 EMA_RATE = 0.99
八、運(yùn)行模型
至此,模型構(gòu)建完成。模型的運(yùn)行步驟大致如下:
1.確保數(shù)據(jù)文件放在了對(duì)應(yīng)路徑中,運(yùn)行python process_data對(duì)數(shù)據(jù)進(jìn)行預(yù)處理。
2.運(yùn)行python train.py對(duì)模型進(jìn)行訓(xùn)練,訓(xùn)練好的模型會(huì)自動(dòng)保存到對(duì)應(yīng)的路徑中。
3.運(yùn)行python eval.py讀取保存的最新模型,對(duì)訓(xùn)練/開發(fā)/測(cè)試集進(jìn)行驗(yàn)證。
我簡(jiǎn)單跑了一下,由于數(shù)據(jù)集較小,模型的泛化能力不是很好。
當(dāng)訓(xùn)練集、開發(fā)集、測(cè)試集的分布為[0.8,0.1,0.1],訓(xùn)練2000個(gè)batch_size=64的mini_batch時(shí),模型在各數(shù)據(jù)集上的acc表現(xiàn)大致如下:
訓(xùn)練集 0.95
開發(fā)集 0.79
測(cè)試集 0.80
更多
轉(zhuǎn)行做機(jī)器學(xué)習(xí),要學(xué)的還很多,文中如有錯(cuò)誤紕漏之處,懇請(qǐng)諸位大佬拍磚指教…
項(xiàng)目GitHub地址:https://github.com/AaronJny/emotional_classification_with_rnn
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- 深度學(xué)習(xí)TextRNN的tensorflow1.14實(shí)現(xiàn)示例
- python循環(huán)神經(jīng)網(wǎng)絡(luò)RNN函數(shù)tf.nn.dynamic_rnn使用
- python人工智能tensorflow構(gòu)建循環(huán)神經(jīng)網(wǎng)絡(luò)RNN
- Python使用循環(huán)神經(jīng)網(wǎng)絡(luò)解決文本分類問題的方法詳解
- 基于循環(huán)神經(jīng)網(wǎng)絡(luò)(RNN)的古詩(shī)生成器
- TensorFlow實(shí)現(xiàn)RNN循環(huán)神經(jīng)網(wǎng)絡(luò)
- 循環(huán)神經(jīng)網(wǎng)絡(luò)TextRNN實(shí)現(xiàn)情感短文本分類任務(wù)
相關(guān)文章
keras模型保存為tensorflow的二進(jìn)制模型方式
這篇文章主要介紹了keras模型保存為tensorflow的二進(jìn)制模型方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來(lái)看看吧2020-05-05Python+?Flask實(shí)現(xiàn)Mock?Server詳情
這篇文章主要介紹了Python+?Flask實(shí)現(xiàn)Mock?Server詳情,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-09-09關(guān)于django python manage.py startapp 應(yīng)用名出錯(cuò)異常原因解析
這篇文章主要介紹了關(guān)于django python manage.py startapp 應(yīng)用名出錯(cuò)異常原因解析,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12安裝python-docx后,無(wú)法在pycharm中導(dǎo)入的解決方案
這篇文章主要介紹了安裝python-docx后,無(wú)法在pycharm中導(dǎo)入的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來(lái)看看吧2021-03-03python中sort sorted reverse reversed函數(shù)的區(qū)別說明
這篇文章主要介紹了python中sort sorted reverse reversed函數(shù)的區(qū)別說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來(lái)看看吧2020-05-05通過PHP與Python代碼對(duì)比的語(yǔ)法差異詳解
這篇文章主要介紹了通過PHP與Python代碼對(duì)比淺析語(yǔ)法差異,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-07-07