日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

Deep-Learning-with-Python] 文本序列中的深度学习

發(fā)布時間:2024/4/24 python 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Deep-Learning-with-Python] 文本序列中的深度学习 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

?

?

?

  • 將文本數據處理成有用的數據表示
  • 循環(huán)神經網絡
  • 使用1D卷積處理序列數據

深度學習模型可以處理文本序列、時間序列、一般性序列數據等等。處理序列數據的兩個基本深度學習算法是循環(huán)神經網絡和1D卷積(2D卷積的一維模式)。

文本數據

文本是最廣泛的序列數據形式。可以理解為一系列字符或一系列單詞,但最經常處理的是單詞層面。自然語言處理的深度學習是應用在單詞、句子或段落上的模式識別;就像計算機視覺是應用在像素上的模式識別。

就像其他神經網絡一樣,深度學習模型不能直接處理原始文本:只能處理數值型張量。文本向量化是指將文本轉換成數值型張量的過程。有多種處理方式:?
- 將文本分割成單詞,將每個單詞轉換成一個向量;?
- 將文本分割成字符,將每個字符轉換成一個向量;?
- 抽取單詞或字符的n-grams,將每個n-grams轉換成一個向量;n-grams是多個連續(xù)單詞或字符的重疊組。

總的來說,可以文本分解的基本的不同單元(單詞,字符或n元語法)稱為標記,將文本分解為這樣的標記的過程稱為標記化tokenization。文本向量化過程:對文本使用標記模式,將數值向量和生成的token聯(lián)系起來。這些向量打包成序列張量,送到深度學習網絡中。將向量和token對應方式有多種,比如one-hot encoding for tokens和token embedding(word embedding).

單詞、字符的one-hot編碼

將token向量化最常見、最基本的方法是one-hot編碼。?
單詞級別one-hot編碼

import numpy as npsamples = ['The cat sat on the mat.', 'The dog ate my homework.']token_index = {}#對應關系token-ids for sample in samples:for word in sample.split():if word not in token_index: #字典中不存在token時,添加token token_index[word] = len(token_index)+1 #不使用0max_length = 10#處理單句的最大長度 results = np.zeros(shape=(len(samples),max_length,max(token_index.values())+1))#所有樣本向量化保存結果 for i, sample in enumerate(samples):for j, word in list(enumerate(sample.split()))[:max_length]:#如果句子長度過長,截斷;j句子第j單詞index = token_index.get(word)#字典中位置results[i,j,index] = 1.
  • ?

字符級one-hot編碼

import stringsamples = ['The cat sat on the mat.', 'The dog ate my homework.'] characters = string.printable#包含所有可打印字符的字符串 token_index = dict(zip(characters,range(1,len(characters)+1)))#id-character;1計數max_length = 50#處理單個句子的字符最大長度 results = np.zeros(shape=(len(samples),max_length,max(token_index.keys())+1))for i, sample in enumerate(samples):for j, character in enumerate(sample):#將句子看做字符的集合,而不是單詞index = token_index.get(character)results[i,j,index] = 1.
  • ?

Keras內置有文本單詞級和字符集one-hot編碼函數,從原始文本數據開始處理。推薦使用這些函數,因為它們考慮了許多重要的特性,比如忽略字符串中的個別特殊字符,只考慮數據集中最常見的N個單詞(避免處理非常大的輸入向量空間)。?
Keras內置函數的單詞級one-hot編碼

from keras.preprocessing.text import Tokenizersamples=['The cat sat on the mat.','The dog ate my homework.']tokenizer = Tokenizer(num_words=1000)#考慮1000個最常見的單詞 tokenizer.fit_on_texts(samples)#生成word indexsequences = tokenizer.texts_to_sequences(samples)#將文本轉換成下標列表one_hot_results = tokenizer.texts_to_matrix(samples,mode='binary')#文本直接轉換為one-hot編碼,向量word_index= tokenizer.word_index#學到的word index對應關系 print('Found %s unique tokens.' % len(word_index))
  • ?

單熱編碼的變體是單熱哈希編碼—當詞匯表中的唯一token數量太大而無法明確處理時,可以使用該技巧。可以將單詞散列為固定大小的向量,而不是為每個單詞顯式分配索引并在字典中保留這些索引的引用。這通常使用非常輕量級的散列函數來完成。這種方法的主要優(yōu)點是它不需要維護一個明確的單詞索引,這可以節(jié)省內存并允許數據的在線編碼(可以在看到所有可用數據之前立即生成token向量)。這種方法的一個缺點是它容易受到哈希沖突的影響:兩個不同的詞可能最終會有相同的哈希值,隨后任何查看這些哈希值的機器學習模型都無法區(qū)分這些詞。當散列空間的維度遠大于被散列的唯一token的總數時,散列沖突的可能性降低。

samples = ['The cat sat on the mat.', 'The dog ate my homework.']dimensionality = 1000#hash空間維度 max_length = 10#處理單個句子長度results = np.zeros((len(samples), max_length, dimensionality)) for i, sample in enumerate(samples):for j, word in list(enumerate(sample.split()))[:max_length]:index = abs(hash(word)) % dimensionalityresults[i, j, index] = 1.
  • ?

word embeddings

將向量與單詞相關聯(lián)的另一種流行且有效的方法是使用密集單詞向量,也稱為詞嵌入。通過單熱編碼獲得的向量是二進制的,稀疏的(主要由零組成),并且具有非常高的維度(與詞匯表中的單詞數相同的維度),詞嵌入是低維浮點向量(即密集向量,與稀疏向量相反).與通過單熱編碼獲得的單詞向量不同,詞嵌入是從數據中學習的。在處理非常大的詞匯表時,通常會看到256維,512維或1,024維的單詞嵌入。另一方面,單熱編碼字通常導致向量維度是20000或更大(在這種情況下捕獲20000token的詞匯標)。因此,詞嵌入將更多信息打包到更少的維度中。

詞嵌入有兩種獲得方式:?
- 學習詞嵌入和關注的主要任務(例如文檔分類或情緒預測)聯(lián)合起來。在此設置中,從隨機單詞向量開始,然后以與神經網絡權重相同的方式學習單詞向量;?
- 加載到模型詞嵌入中,這些詞是使用不同的機器學習任務預先計算出來的,而不是正在嘗試解決的任務。這些被稱為預訓練詞嵌入。

通過Embedding網絡層學習詞嵌入向量

將密集向量與單詞相關聯(lián)的最簡單方法是隨機選擇向量。這種方法的問題在于產生的嵌入空間沒有結構:例如,accurate和exact的單詞最終可能會有完全不同的嵌入,即使它們在大多數句子中都是可互換的。深度神經網絡難以理解這種嘈雜的非結構化嵌入空間。?
更抽象的說,詞向量之間的幾何關系應該反映這些單詞之間的語義關系。詞嵌入意味著將自然語言映射到幾何空間中。比如,在適合的嵌入空間中,希望將同義詞嵌入到相似的單詞向量中;一般來說,期望任意兩個單詞向量之間的幾何距離(例如L2距離)與相關單詞之間的語義距離相關(意思不同的單詞嵌入在遠離彼此相關,而相關的詞更接近)。除了距離之外,可能希望嵌入空間中的特定方向有意義。?
是否有一些理想的單詞嵌入空間可以完美地映射人類語言,并且可以用于任何自然語言處理任務?可能,但尚未計算任何類型的東西。此外,沒有人類語言這樣的東西—有許多不同的語言,它們不是同構的,因為語言是特定文化和特定語境的反映。但更務實的是,良好的詞匯嵌入空間在很大程度上取決于你的任務:英語電影評論情感分析模型的完美詞匯嵌入空間可能與英語法律的文檔分類模型的完美嵌入空間有所不同,因為某些語義關系的重要性因任務而異。

因此,在每個新任務中學習新的嵌入空間是合理的。幸運的是,反向傳播使這很容易,而Keras使它變得更加容易。它是關于學習圖層的權重:Embedding嵌入圖層。

from keras.layers import Embeddingembedding_layer = Embedding(1000,64)#嵌入層需要至少兩個參數:tokens個數eg1000,嵌入層維度eg64
  • 1
  • 2
  • 3

Embedding嵌入層最好的理解方法是看成一個字典:將整數下標(代表一個某個單詞)映射到一個稠密向量上。它將整數作為輸入,它在內部字典中查找這些整數,并返回相關的向量。?

Embedding網絡層接收一個2D整數張量為輸入,形狀(samples,sequence_length),其中每個實體是整數的序列。它可以嵌入可變長度的序列:例如,可以在前面的示例批次中輸入嵌入層,其中包含形狀(32,10)(32個序列長度為10的批次)或(64,15)(64個序列長度15的批次)。但是,批處理中的所有序列必須具有相同的長度(因為需要將它們打包到單個張量中),因此比其他序列短的序列應該用零填充,并且應該截斷更長的序列。?
網絡層返回一個3D浮點類型張量,形狀(samples, sequence_length, embedding_dimensionality).這樣的3D張量可以用RNN或1D卷積層處理。?
當實例化一個Embedding網絡層時,權重(內部字典的token向量)和其他網絡層類似,隨機初始化。在訓練過程中,這些詞向量通過反向傳播逐漸改動,將空間結構化為下游模型可以利用的東西。一旦完全訓練,嵌入空間將顯示許多結構 —一種專門針對正在訓練模型的特定問題的結構。?
在IMDB電影評論語義分析任務上,應用詞嵌入。首先,在電影評論中取最常見的10000個單詞,然后將每條評論長度限制為20個單詞。網絡將會學習到10000個單詞的8維詞嵌入空間,將每個輸入的整數序列(2D)轉換成嵌入層序列(3D浮點張量),平鋪成2D張量,添加一個Dense層做分類。

from keras.datasets import imdb from keras import preprocessingmax_features = 10000#處理的單詞數目 maxlen = 20#單個句子最大長度(x_train,y_train),(x_test,y_test) = imdb.load_data(num_words=max_features)#數據為整數列表x_train = preprocessing.sequence.pad_sequences(x_train,maxlen=maxlen)#轉換為張量,(samples,maxlen) x_test = preprocessing.sequence.pad_sequences(x_test, maxlen=maxlen)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

使用Embedding層分類

from keras.models import Sequential from keras.layers import Flatten,Densemodel = Sequential() model.add(Embedding(10000,8,input_length=maxlen)) model.add(Flatten()) model.add(Dense(1,activation='sigmoid')) model.compile(optimizer='rmsprop',loss='binary_crossentropy',metrics=['acc'])history = model.fit(x_train,y_train,epochs=10,batch_size=32,validation_split=0.2)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

驗證集上的準確率為76%左右,考慮到每條評論只有20個單詞,這個結果也可以接受。注意僅僅將embedded嵌入序列平鋪,然后在單層全連接網絡上訓練,導致模型將輸入序列的每個單詞分割開來看,沒有考慮句子的結構以及單詞之間的關系。最好在嵌入序列的頂部添加循環(huán)層或1D卷積層,以學習將每個序列作為一個整體考慮在內的特征。

使用預訓練詞嵌入

有時,只有很少的訓練數據,無法單獨使用數據來學習特定的任務的詞嵌入,怎么辦?可以從預先計算的嵌入空間中加載嵌入向量,而不是學習想要解決的問題的詞嵌入向量,這些嵌入空間是高度結構化的并且展示了有用的屬性 - 它捕獲了語言結構的一般方面。在自然語言處理中使用預訓練單詞嵌入的基本原理與在圖像分類中使用預訓練的卷積網絡大致相同:沒有足夠的數據可用于自己學習真正有用的特征,但期望獲得所需的特征相當通用—即常見的視覺特征或語義特征。在這種情況下,重用在不同問題上學習的特征是有意義的。

這樣的詞嵌入通常使用詞出現統(tǒng)計(關于在句子或文檔中共同出現的詞的觀察),使用各種技術來計算,一些涉及神經網絡,一些不涉及。Bengio等人最初探討了以無人監(jiān)督的方式計算密集,低維度的文字嵌入空間的想法。在21世紀初期,發(fā)布了最著名和最成功的詞匯嵌入方案之后:Word2vec算法,它開始在研究和行業(yè)中廣泛應用,由Tomas Mikolov于2013年在谷歌開發(fā).?Word2vec維度捕獲具體語義屬性,例如性別。?
可以在Keras嵌入層中下載和使用各種預嵌入的字嵌入數據庫。 Word2vec就是其中之一。另一種流行的稱為全球向量詞表示GloVe,由斯坦福大學的研究人員于2014年開發(fā)。該嵌入技術基于對詞共現統(tǒng)計矩陣進行因式分解,已經為數以百萬計的英語token提供了預先計算的嵌入,這些嵌入是從維基百科數據和通用爬網數據中獲得的。

整合:原始文本到詞嵌入

下載IMDB原始數據集?
地址,解壓縮。

處理原始數據

import osimdb_dir = './imdb'#數據集地址 train_dir = os.path.join(imdb,'train')#訓練集地址labels = []#保存標簽 texts = []#保存原始數據for label_type in ['neg','pos']:dir_name = os.path.join(train_dir,label_type)for fname in os.listdir(dir_name):if fname[-4:] == '.txt':#確保文件格式正確f = open(os.path.join(dir_name,fname))texts.append(f.read())#讀取文本內容f.close()if label_type == 'neg':#保存標簽labels.append(0)else:labels.append(1)
  • ?

數據分詞tokenizing?
文本向量化,劃分訓練集和驗證集。因為預訓練的單詞嵌入對于幾乎沒有可用訓練數據的問題特別有用(否則,任務特定的嵌入表現可能超過它們),將添加限制:將訓練數據限制為前200個樣本。因此,在查看了200個示例之后,對電影評論進行分類。

from keras.preprocessing.text import Tokenizer from keras.preprocessing.sequence import pad_sequences import numpy as npmaxlen = 100#單個句子最大長度 training_samples = 200#訓練集數據量 validation_samples = 10000#驗證集數據量 max_words = 10000#字典長度tokenizer = Tokenizer(num_words=max_words) tokenizer.fit_on_texts(texts)#生成tokens字典 sequences = tokenizer.texts_to_sequences(texts)#將多個文檔轉換為字典對應下標的list表示,shape為(文檔數,每條文檔的長度) word_index = tokenizer.word_index#word-id字典 print('Found %s unique tokens' % len(word_index))data = pad_sequences(sequences, maxlen=maxlen)#將每個序列padding成相同長度labels = np.asarray(labels) print('Shape of data tensor:',data.shape) print('Shape of label tensor:', labels.shape)indices = np.arange(data.shape[0])#打亂shuffle np.random.shuffle(indices) data = data[indices] labels = labels[indices]x_train = data[:training_samples]#劃分訓練集和驗證集 y_train = labels[:training_samples] x_val = data[training_samples:training_samples+validation_samples] y_val = labels[training_samples:training_samples+validaion_samples]
  • ?

下載GLOVE詞嵌入向量?
地址;2014英語維基百科,822MB zip文件,名字‘glove.6B.zip’,包括100維的嵌入向量,40萬個單詞。?
預處理嵌入向量?
讀取txt文件,構建一個映射關系:單詞–詞向量。

glove_dir = './glove.6B'embeddings_index = {} f = open(os.path.join(glove_dir,'glove.6B.100d.txt')) for line in f:values = line.split()word = values[0]coefs = np.asarray(values[1:],dtype='float32')embeddings_index[word] = coefsf.close()print('Found %s word vectors.' % len(embeddings_index))
  • ?

之后,生成一個嵌入矩陣,加載到Embedding網絡層中,形狀(max_words,embedding_dims),其中其中每個條目i包含參考詞索引(在tokenization期間構建)中索引i的單詞的embedding_dim維的向量。請注意,索引0不應代表任何單詞或標記 - 它是占位符。?
預處理GloVe詞向量

embedding_dims = 100embedding_matrix = np.zeros((max_words,embedding_dims)) for word, i in word_index.items():if i < max_words:embedding_vector = embeddings_index.get(word)if embedding_vector is not None:embedding_matrix[i] = embedding_vector
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

模型定義

from keras.models import Sequential from keras.layers import Embedding,Flatten,Densemodel=Sequential() model.add(Embedding(max_words, embedding_dim, input_length=maxlen)) #字典長度,輸出維度,句子長度 model.add(Flatten()) model.add(Dense(32,activation='relu')) model.add(Dense(1,activation='sigmoid'))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

加載預訓練的詞向量到Embedding網絡層中

model.layers[0].set_weights([embedding_matrix]) model.layers[0].trainable = False
  • 1
  • 2

“Freeze”凍住網絡層–不可訓練。

模型訓練驗證

model.compile(optimizer='rmsprop',loss='binary_crossentropy',metrics=['acc']) history = model.fit(x_train, y_train,epochs=10,batch_size=32, validation_data=(x_val, y_val))model.save_weights('pre_trained_glove_model.h5')
  • 1
  • 2
  • 3
  • 4
  • 5

模型訓練集和驗證集上的準確率、損失值變化?

該模型很快就開始過度擬合—訓練樣本數量很少。出于同樣的原因,驗證準確性具有很大的差異。

請注意,結果可能會有所不同:因為訓練樣本很少,性能很大程度上取決于選擇的200個樣本—而且是隨意選擇。?
也可以訓練相同的模型,而無需加載預訓練的單詞嵌入,也不凍結嵌入層。在這種情況下,您將學習輸入tokens的特定于任務的嵌入,當大量數據可用時,這通常比預訓練的詞嵌入更強大。

不用預訓練詞嵌入訓練相同的網絡模型

from keras.models import Sequential from keras.layers import Embedding, Flatten, Densemodel = Sequential()model.add(Embedding(max_words, embedding_dim, input_length=maxlen)) model.add(Flatten()) model.add(Dense(32, activation='relu')) model.add(Dense(1, activation='sigmoid'))model.compile(optimizer='rmsprop',loss='binary_crossentropy',metrics=['acc']) history = model.fit(x_train, y_train,epochs=10,batch_size=32,validation_data=(x_val, y_val))
  • ?

訓練集和驗證集準確率、損失值變化?

驗證準確性在50%內停滯。因此,在這種情況下,預訓練的單詞嵌入優(yōu)于共同學習的嵌入。

測試集上評估

test_dir = os.path.join(imdb_dir, 'test')#處理測試數據labels = [] texts = []for label_type in ['neg', 'pos']:dir_name = os.path.join(test_dir, label_type)for fname in sorted(os.listdir(dir_name)):if fname[-4:] == '.txt':f = open(os.path.join(dir_name, fname))texts.append(f.read())f.close()if label_type == 'neg':labels.append(0)else:labels.append(1)sequences = tokenizer.texts_to_sequences(texts) x_test = pad_sequences(sequences, maxlen=maxlen) y_test = np.asarray(labels)model.load_weights('pre_trained_glove_model.h5') model.evaluate(x_test, y_test)#準確率在56%左右。
  • ?

小結

  • 將原始數據轉換成網絡可以處理的張量;
  • 在Keras模型中使用Embedding網絡層;
  • 在自然語言處理的小數據集問題上使用預訓練的詞向量提高模型準確率。

循環(huán)神經網絡Recurrent neural networks[RNN]

到目前為止,所見過的所有神經網絡的一個主要特征,例如全連接的網絡和卷積網絡,就是它們沒有“記憶能力”。顯示給它們的每個輸入都是獨立處理的,輸入之間沒有任何狀態(tài)。使用此類網絡,為了處理序列或時間序列的數據,必須立即向網絡顯示整個序列:將其轉換為單個數據點。例如,在IMDB示例中所做的:整個電影評論被轉換為單個大型向量并一次處理。這種網絡稱為前饋網絡。

相比之下,當你正在閱讀現在的句子時,你正在逐字處理它 - 或者更確切地說,通過眼睛掃視 - 同時記住之前的事物;這使你能夠流暢地表達這句話所傳達的意義。生物智能逐步處理信息,同時保持其處理內部模型,根據過去的信息構建,并隨著新信息的不斷更新而不斷更新。

遞歸神經網絡(RNN)采用相同的原理,盡管是極其簡化的版本:它通過迭代序列元素并維持包含與迄今為止所見內容相關信息的狀態(tài)來處理序列。?實際上,RNN是一種具有內部循環(huán)的神經網絡. 在處理兩個不同的獨立序列(例如兩個不同的IMDB評論)之間重置RNN的狀態(tài),因此仍然將一個序列視為單個數據點:網絡的單個輸入。?更改的是,數據點不再在一個步驟中處理;相反,網絡內部循環(huán)遍歷序列元素。?
?
為了使這些循環(huán)loop和狀態(tài)state的概念清晰,用Numpy實現一個小的RNN的前向傳遞。該RNN將一系列向量作為輸入,您將其編碼為2D張量大小(timesteps, input_features)。它在時間步長上循環(huán),并且在每個時間步長,它在t處考慮其當前狀態(tài),在t處考慮輸入,形狀(input_features, ),并將它們組合起來以獲得t處的輸出。然后,將設置下一步的狀態(tài)為此前一個輸出。對于第一個時間步,未定義前一個輸出;因此,目前沒有狀態(tài)。所以,把狀態(tài)初始化為零向量稱為網絡的初始狀態(tài)。

偽代碼?
V1

state_t = 0 #初始t:0 for input_t in input_sequence:#序列元素迭代output_t = f(input_t,state_t)#輸出為當前輸入和當前狀態(tài)相關state_t = output_t#下一刻的狀態(tài)為上一刻狀態(tài)的輸出
  • 1
  • 2
  • 3
  • 4

可以具體化函數f:將輸入和狀態(tài)轉換為輸出—參數化為兩個矩陣W和U以及偏置向量。類似于前饋網絡中全連接層操作的轉換。

V2

state_t = 0 for input_t in input_sequences:output_t = activation(dot(W,input_t)+dot(U,state_t)+b)state_t = output_t
  • 1
  • 2
  • 3
  • 4

V3

import numpy as nptimesteps = 100 input_features = 32 output_features = 64inputs = np.zeros((timesteps,input_features)) state_t = np.zeros((output_features,))W = np.random.random((output_features,input_features)) U = np.random.random((output_features,output_features)) b = np.random.random((output_features,))successive_outputs = [] for input_t in inputs:output_t = np.tanh(np.dot(W,input_t)+np.dot(U,state_t)+b)successive_outputs.append(output_t)state_t = output_tfinal_output_sequence = np.concatenate(successive_outputs,axis=0)
  • ?

總之,RNN是一個for循環(huán),它重用循環(huán)的前一次迭代期間計算的結果,僅此而已。當然,適合這個定義有許多不同的RNN - 這個例子就是其中之一最簡單的RNN。RNN的特征在于它們的階躍函數,例如在這種情況下的以下函數:?
outputt=np.tanh(np.dot(W,inputt)+np.dot(U,statet)+b)outputt=np.tanh(np.dot(W,inputt)+np.dot(U,statet)+b)

注意:在該示例中,最終輸出是2D張量的形狀(timesteps,output_features),其中每個時間步長是時間t處的循環(huán)的輸出結果。輸出張量中的每個時間步t包含關于輸入序列中的時間步長0到t的信息 - 關于整個過去。因此,在許多情況下,不需要這個完整的輸出序列;你只需要最后一個輸出(循環(huán)結束時的output_t),因為它已經包含有關整個序列的信息。

Keras 循環(huán)網絡層

上面numpy編碼實現的是Keras網絡層—SimpleRNN網絡層:

from keras.layers import SimpleRNN
  • 1

有一個小區(qū)別是:SimpleRNN網絡層處理序列小批量,而不是一個簡單的numpy序列,意味著輸入形狀為(batch_size, timesteps, input_features),不是(timesteps, input_features).?
和Keras的其他循環(huán)網絡類似,SimpleRNN有兩種運行方式:返回每個時間步的輸出結果序列集,3D張量,形狀(batch_size, timesteps, output_features);返回每個輸入序列的最終輸出結果,2D張量,形狀(batch_size, output_features). 兩種方式通過參數return_sequences 控制。?
使用SimpleRNN,返回最后時間步的輸出結果:

from keras.models import Sequential from keras.layers import Embedding,SimpleRNNmodel = Sequential() model.add(Embedding(10000,32)) model.add(SimpleRNN(32))
  • ?

返回全部的狀態(tài)序列state:

model = Sequential() model.add(Embedding(10000,32)) model.add(SimpleRNN(32,return_sequences=True))
  • ?

有時候,需要將幾個循環(huán)網絡層依次相連,增加網絡模型的特征表示能力。同時,為了返回所有的輸出序列,必須獲得所有的中間網絡層結果。

model = Sequential() model.add(Embedding(10000,32)) model.add(SimpleRNN(32,return_sequences=True)) model.add(SimpleRNN(32,return_sequences=True)) model.add(SimpleRNN(32,return_sequences=True)) model.add(SimpleRNN(32))
  • ?

使用循環(huán)網絡處理IMDB數據集。?
數據集處理

from keras.datasets import imdb from keras.preprocessing import sequencemax_fetaures = 10000 maxlen = 500 batch_size = 32 print("Loading data...") (x_train,y_train),(x_test,y_test)=imdb.load_data(num_words=max_features) print(len(x_train),'train sequences') print(len(x_test),'test sequences')print('Pad sequences (sample x time)') x_train = sequence.pad_sequences(x_train,maxlen=maxlen) x_test = sequence.pad_sequences(x_test,maxlen=maxlen)print('x_train shape:', x_train.shape) print('x_test shape:',x_test.shape)
  • ?

模型訓練

from keras.layers import SimpleRNNmodel = Sequential() model.add(Embedding(max_features,32)) model.add(SimpleRNN(32)) model.add(Dense(1,activation='sigmoid'))model.compile(optimizer='rmsprop',loss='binary_crossentropy',metrics=['acc']) history=model.fit(x_train,y_train,epochs=10,batch_size=128,validation_split=0.2)
  • ?

訓練接、驗證集上準確率、損失值變化?

簡單的SimpleRNN驗證集上準確率最高85%左右,主要問題在于輸入序列只考慮前500個單詞,而不是整個完整序列。SimpleRNN不擅長處理長序列,如文本。常用其他循環(huán)網絡處理。

LSTM和GRU網絡層

SimpleRNN并不是Keras唯一的循環(huán)網絡層,還有LSTM和GRU。實際應用時,通常不使用SimpleRNN,因為SimpleRNN過于簡單,無法實際使用。SimpleRNN有一個主要問題:雖然它理論上應該能夠在時間t保留有關輸入的信息[這些信息在很多時間之前看到],但在實踐中,這種長期依賴性是不可能學習到的。?這是由于梯度消失問題,類似于非循環(huán)網絡(前饋網絡)所觀察到的:當不斷向網絡添加層時,網絡最終變得無法處理。LSTM和GRU層旨在解決梯度消失問題。

LSTM,Long Short-Term Memory,SimpleRNN的變種:它增加了一種跨多個時間步攜帶信息的方法。?想象一下,傳送帶與正在處理的序列平行運行。序列中的信息可以在任何時候跳到傳送帶上,運輸到稍后的時間步,并在需要時完好無損地跳下。這基本上就是LSTM所做的事情:它為以后保存信息,從而防止舊信號在處理過程中逐漸消失。

為了詳細了解這一點,讓我們從SimpleRNN單元格開始。因為有很多權重矩陣,所以用單詞o(Wo和Uo)索引單元格中用于輸出的W和U矩陣。?

在此圖片中添加一個跨時間步長傳輸信息的附加數據流。不同的時間步長Ct各不相同,其中C代表Carry。此信息將對單元格產生以下影響:它將與輸入連接和循環(huán)連接相結合(通過全連接轉換:帶有權重矩陣的點積,然后是偏置加法和激活函數),它將影響被發(fā)送到下一個時間步的狀態(tài)(通過激活函數和乘法運算)。從概念上講,信息數據流是一種調制下一個輸出和下一個狀態(tài)的方法。?
?
微妙之處:計算Ct數據流的下一個值的方式。涉及三種不同的轉變。這三種都具有SimpleRNN單元的形式:

y = activation(dot(state_t,U)+dot(input_t,W)+b)
  • 1

但三種轉換方式都有自己的權重矩陣,用i, f, k 對三種方式索引。

output_t=activation(dot(state_t,Uo)+dot(input_t,Wo)+dot(C_t,Vo)+bo)i_t = activation(dot(state_t, Ui) + dot(input_t, Wi) + bi) f_t = activation(dot(state_t, Uf) + dot(input_t, Wf) + bf) k_t = activation(dot(state_t, Uk) + dot(input_t, Wk) + bk)
  • 1
  • 2
  • 3
  • 4
  • 5

通過組合i_t,f_t和k_t獲得新的carray狀態(tài)(next c_t)。

c_t+1 = i_t * k_t + c_t * f_t
  • 1

如果想直覺性地了解,可以解釋每個操作的意圖。例如,可以說乘以c_t和f_t是故意忘記carry數據流中無關信息的一種方法;同時,i_t和k_t提供有關當前的信息,用新信息更新carry軌道。但是這些解釋并沒有多大意義,因為這些操作實際上做的是由參數化的權重決定的;并且權重以端到端的方式學習,從每輪訓練開始—不可能將這個或那個操作歸功于特定目的。RNN單元格的規(guī)范確定了假設空間—在訓練期間搜索良好模型配置的空間 - 但它不能確定單元格的作用;這取決于單元格權重。(如全連接網絡確定假設空間,全連接權重系數決定每次轉換操作)。具有不同重量的相同單元可以做非常不同的事情。因此,構成RNN單元的操作組合可以更好地解釋為對空間搜索的一組約束,而不是工程意義上的設計。

對于研究人員來說,‘ 如何實現RNN單元的問題’似乎選擇約束方式, 最好留給優(yōu)化算法(如遺傳算法或強化學習過程),而不是人類工程師。在未來,這就是構建網絡的方式。總之,不需要了解LSTM單元的特定架構。LSTM單元的作用:允許以后重新注入過去的信息,從而解決消失梯度問題。

LSTM例子

IMDB數據集上使用LSTM.網絡模型和SimpleRNN架構類似。設置LSTM網絡層輸出維度,其他為默認設置。Keras默認參數設置,不需要微調即可取得很好的效果。

from keras.layers import LSTMmodel = Sequential() model.add(Embedding(10000,32)) model.add(LSTM(32)) model.add(Dense(1,activation='sigmoid'))model.compile(optimizer='rmsprop',loss='binary_crossentropy',metrics=['acc']) history = model.fit(x_train,y_train,epochs=10,batch_size=128,validation_split=0.2)
  • ?

訓練集、驗證集損失值、準確率變化?
?
驗證集上準確率在89%左右。比SimpleRNN結果好很多,因為梯度消失問題對LSTM影響很小。但是這種結果對于這種計算密集型方法并不具有開創(chuàng)性。為什么LSTM表現不佳?一個原因是沒有?
調整超參數,例如嵌入維度或LSTM輸出維度。另一種可能是缺乏正則化。但主要原因是分析評論的長期結構(LSTM擅長什么)對情緒分析問題沒有幫助。通過查看每個評論中出現的單詞以及頻率,可以很好地解決這樣一個基本問題。這就是第一個全連接的方法。?
但是有更難的自然語言處理問題在那里,LSTM的優(yōu)勢將變得明顯:特別是問答和機器翻譯

小結

  • RNN結構,如何工作?
  • LSTM
  • Keras LSTM處理序列數據

循環(huán)神經網絡的高級應用

  • 循環(huán)網絡Dropout:緩解過擬合
  • stacking 循環(huán)網絡:增加模型特征表示能力;
  • 雙向循環(huán)網絡:以不同的方式向循環(huán)網絡提供相同的信息,提高準確性并減少遺忘。

溫度預測問題

到目前為止,所涵蓋的唯一序列數據是文本數據,例如IMDB數據集和路透社數據集。但是,除了語言處理之外,序列數據還存在于更多問題中。比如德國耶拿馬克斯普朗克生物地球化學研究所氣象站記錄的天氣時間序列數據集

在該數據集中,記錄幾年內每10分鐘14種不同的度量(例如空氣溫度,大氣壓,濕度,風向等)。原始數據可以追溯到2003年,但這個例子僅限于2009 - 2016年的數據。該數據集非常適合學習使用數值時間序列。使用它來構建一個模型,該模型將最近的一些數據作為輸入過去(幾天的數據點)并預測未來24小時的氣溫。

下載地址

觀察數據

import osdata_dir = '/home/gao/datasets/jena_climate' fname = os.path.join(data_dir,'jena_climate_2009_2016.csv')f = open(fname) data = f.read() f.close()lines = data.split('\n') header = lines[0].split(',') lines = lines[1:]print(header) print(len(lines))
  • ?

一共有420551條記錄,每行是一個時間步:日期和14個和天氣相關的記錄值。headers:

["Date Time", "p (mbar)", "T (degC)", "Tpot (K)", "Tdew (degC)", "rh (%)", "VPmax (mbar)", "VPact (mbar)", "VPdef (mbar)", "sh (g/kg)", "H2OC (mmol/mol)", "rho (g/m**3)", "wv (m/s)", "max. wv (m/s)", "wd (deg)"]
  • ?
  • ?

將數據轉換成Numpy數組:

import numpy as npfloat_data = np.zeros((len(lines),len(headers)-1)) for i,line in enumerate(lines):values = [float(x) for x in line.split(',')[1:]]float_data[i,:] = values
  • ?

每10分鐘一條記錄,1天144條數據。畫圖顯示前10天溫度變化情況。

from matplotlib.pyplot as plttemp = float_data[:, 1] plt.plot(range(1440), temp[:1440])
  • ?

如果在過去幾個月的數據中嘗試預測下個月的平均溫度,由于數據的年度可靠周期性,問題將很容易。但是,在幾天的時間內查看數據,溫度看起來更加混亂。這個時間序列是否可以在日常范圍內預測?

準備數據

問題的確切表述如下:給定的數據可以追溯到回溯時間步長(時間步長為10分鐘)并按步驟時間步長采樣,能預測延遲時間步長的溫度嗎??
- lookback:720,查看過去5天數據;?
- steps:6,每小時進行一次數據采樣;?
- delay:144,將來24小時的預測。

開始之前需要:?
1. 將數據預處理為神經網絡可以處理的格式。數據已經是數字,因此不需要進行任何向量化。但是數據中的每個時間序列都有不同的取值范圍(例如,溫度通常介于-20和+30之間,但是以mbar為單位測量的大氣壓力大約為1,000)。 獨立標準化每個時間序列,以便它們都以相似的比例獲取小值。?
2. 編寫一個Python生成器,它接收當前浮點數據數組,并從最近的過去產生批量數據,以及將來的目標溫度。因為數據集中的樣本是高度冗余的(樣本N和樣本N + 1將具有共同的大多數時間步長,明確分配每個樣本將是浪費的。相反,您將使用原始數據動態(tài)生成樣本。

通過減去每個時間序列的平均值并除以標準差來預處理數據。將使用前200,000個步驟作為訓練數據,因此僅計算此部分數據的平均值和標準差。?
數據標準化

mean = float_data[:200000].mean(axis=0) float_data -= mean std = float_data[:200000].std(axis=0) float_data /= std
  • ?

數據生成器生成元組形式,(samples,targets),samples是輸入數據的一個批量,targets是對應的溫度標簽數組。參數:
- data:原始浮點數組數據;?
- lookback:輸入數據查看的歷史數據長度;?
- delay:預測將來數據的長度;?
- min_index和max_index:數據數組中的索引,用于分隔要繪制的時間步長timesteps,對于保留一部分數據以進行驗證以及另一部分用于測試非常有用;?
- shuffle:是否打亂順序;?
- batch_size:批量容量大小;?
- step: 用于對數據進行采樣的時間段(以時間步長為單位)。將其設置為6,以便每小時繪制一個數據點。

數據生成器

def generator(data,lookback,delay,min_index,max_index,shuffle=False,batch_size=128,step=6):if max_index is None:max_index = len(data) - delay - 1i = min_index +lookbackwhile 1:if shuffle:rows = np.random.randint(min_index+lookback,max_index,size=batch_size)else:if i + batch_size >= max_index:i = min_index +lookbackrows = np.arange(i,min(i+batch_size,max_index))i += len(rows)samples = np.zeros((len(rows),lookback//step,data.shape[-1]))targets = np.zeros((len(rows),))for j,row in enumerate(rows):indeices = range(rows[j]-lookback,rows[j],step)samples[j] = data[indices]targets[j] = data[rows[j]+delay][1]yield samples, targets
  • ?生成器生成訓練集、驗證集和測試集。訓練集在前200000條數據上,驗證集在之后的100000條數據上,測試集在剩下數據集上。?

準備訓練集、驗證集和測試集

lookback = 1440 step = 6 delay = 144 batch_size = 128train_gen = generator(float_data,lookback=lookback,delay=delay,min_index=0,max_index=200000,shuffle=True,step=step,batch_size=batch_size) val_gen = generator(float_data,lookback=lookback,delay=delay,min_index=200001,max_index=300000,step=step,batch_size=batch_size) test_gen = generator(float_data,lookback=lookback,delay=delay,min_index=300001,max_index=None,step=step,batch_size=batch_size)val_steps = (300000 - 200001 - lookback) test_steps = (len(float_data) - 300001 - lookback)
  • ?

常識性的非機器學習baseline

在開始使用黑盒深度學習模型來解決溫度預測問題之前,先嘗試一種簡單的常識性方法。它將作為一個完整性檢查,它將建立一個你必須擊敗的baseline,以證明更先進的機器學習模型的用處。當你正在接近尚未知解決方案的新問題時,這些常識baseline會很有用。一個典型的例子是不平衡的分類任務,其中一些類比其他類更常見。如果數據集包含90%的A類實例和10%B類實例,則采用常識方法分類任務是在呈現新樣本時始終預測“A”。這樣的分類器總體上是90%準確的,因此任何基于學習的方法都應該超過這個90%的分數以證明有用性。有時,這些基本baseline可能難以擊敗。

在這種情況下,可以安全地假設溫度時間序列是連續(xù)的(明天的溫度可能接近今天的溫度)以及具有每日周期的周期性。因此,常識性的方法是始終預測從現在起24小時的溫度將等于現在的溫度。?
使用平均絕對誤差(MAE)度量來評估這種方法:

np.mean(np.abs(preds-targets))
  • 1

驗證

def evaluate_naive_method():batch_maes = []for step in range(val_steps):samples, targets = next(val_gen)preds = samples[:, -1, 1]mae = np.mean(np.abs(preds - targets))batch_maes.append(mae)print(np.mean(batch_maes))evaluate_naive_method()
  • ?

MAE為0.29.因為數據被處理成0均值,1方差,所以不能立即說明0.29的意義。0.29xtemperature_std轉換為平均絕對誤差2.57。?
MAE轉攝氏度誤差

celsius_error = 0.29*std[1]
  • 1

平均絕對誤差很大。用深度學習解決問題。

機器學習方法

以同樣的方式在嘗試機器學習方法之前建立常識baseline是有用的,在研究復雜且計算成本高昂的模型(如RNN)之前嘗試簡單,廉價的機器學習模型(例如小型,全連接的網絡)是有用的。這是確保解決問題的任何進一步復雜性是合法的并帶來實際好處的最佳方法

下面顯示了一個全連接的模型,該模型從展平數據開始,然后通過兩個Dense網絡層運行。注意最后一個Dense網絡層缺少激活函數,這是回歸問題的典型特征。使用MAE作為損失。因為使用常識方法評估完全相同的數據并使用完全相同的度量標準,結果可以直接進行比較。?

訓練集、驗證集上損失值變化?

一些驗證損失接近無學習基線,但不可靠。這表明首先要有這個baseline的優(yōu)點:事實證明超過baseline并不容易。常識baseline包含許多機器學習模型無法訪問的有價值信息。

RNN baseline

第一個全連接的方法做得不好,但這并不意味著機器學習不適用于這個問題。之前的方法首先使時間序列變平,從輸入數據中刪除了時間概念。數據是一個序列,因果關系和秩序很重要。嘗試循環(huán)序列處理模型 - 它應該是這種序列數據的完美擬合,因為它利用了數據點的時間排序,與第一種方法不同。?
使用GRU網絡層(Gated recurrent unit)。GRU層使用與LSTM相同的原理工作,但它們有些簡化,因此運行成本更低(盡管可能沒有LSTM那么多的特征表示能力)。計算代價和特征表示能力之間的這種權衡在機器學習中隨處可見。

from keras.models import Sequential from keras import layers from keras.optimizers import RMSpropmodel = Sequential() model.add(layers.GRU(32, input_shape=(None, float_data.shape[-1]))) model.add(layers.Dense(1))model.compile(optimizer=RMSprop(), loss='mae') history = model.fit_generator(train_gen,steps_per_epoch=500,epochs=20,validation_data=val_gen,validation_steps=val_steps)
  • ?

?
驗證集上MAE大約為0.265,轉換為攝氏度為2.35.結果好很多。超越常識baseline,展示了機器學習的價值,與此類任務中的序列扁平化全密集網絡相比,循環(huán)網絡的優(yōu)越性。

RNN Dropout

從訓練和驗證曲線可以看出,該模型過度擬合:訓練和驗證損失在幾個epochs之后開始顯著不同。已經熟悉了一種解決這種現象的經典技術:Dropout,它會隨機將一個圖層的輸入單元歸零,以便打破該圖層所暴露的訓練數據中的偶然相關性。但如何在循環(huán)網絡中使用Dropout??在2015年,Yarin Gal作為他關于貝葉斯深度學習的博士論文的一部分,確定了循環(huán)網絡使用dropout的正確方法:應該在每個時間步應用相同的dropout mask(相同的丟棄單位模式),而不是從時間步長到時間步長隨機變化的dropout mask。更重要的是,為了規(guī)范由GRU和LSTM等循環(huán)網絡層形成的特征表示,應將時間上恒定的dropout mask應用在網絡層的內部循環(huán)激活值上。在每個時間步使用相同的dropout mask允許網絡在時間上正確地傳播其學習誤差;時間上隨機的dropout mask會破壞錯誤信號,不利于學習過程。?
GRU + Dropout訓練

from keras.models import Sequential from keras import layers from keras.optimizers import RMSpropmodel = Sequential() model.add(layers.GRU(32,dropout=0.2,recurrent_dropout=0.2,input_shape= (None, float_data.shape[-1])))#dropout輸入數據上應用;recurrent_dropout循環(huán)單元上應用 model.add(layers.Dense(1))model.compile(optimizer=RMSprop(), loss='mae') history = model.fit_generator(train_gen,steps_per_epoch=500,epochs=40,validation_data=val_gen,validation_steps=val_steps)
  • ?

?
在前30個epochs,不再過度擬合。但是,盡管評估分數更穩(wěn)定,但最佳分數并不比以前低很多。

循環(huán)網絡層stack [堆疊]

網絡模型不再過擬合,但特征表示能力成為新的瓶頸。可以增加網絡模型的深度。回想一下通用機器學習工作流程的描述:增加網絡容量通常是一個好主意,直到過度擬合成為主要障礙(假設已經采取基本步驟來緩解過度擬合,例如使用dropout)。?
增加模型容量通常可以增加網絡層神經元數目或者增加網絡層數。循環(huán)層堆疊是構建更強大的循環(huán)網絡的經典方法:例如,目前Google Translate背后就是七個大型LSTM層的堆棧。

要在Keras中將重復層疊加在彼此之上,所有中間層應返回其完整的輸出序列(3D張量),而不是在最后一個時間步的輸出,指定return_sequences = True。

from keras.models import Sequential from keras import layers from keras.optimizers import RMSpropmodel = Sequential() model.add(layers.GRU(32,dropout=0.1,recurrent_dropout=0.5,return_sequences=True,input_shape=(None, float_data.shape[-1]))) model.add(layers.GRU(64, activation='relu',dropout=0.1,recurrent_dropout=0.5)) model.add(layers.Dense(1))model.compile(optimizer=RMSprop(), loss='mae') history = model.fit_generator(train_gen,steps_per_epoch=500,epochs=40,validation_data=val_gen,validation_steps=val_steps)
  • ?

?
結果有所改善,但不太明顯。

雙向RNN

雙向RNN是一種常見的RNN變體,可以在某些任務上提供比常規(guī)RNN更高的性能。它經常用于自然語言處理 - 你可以稱之為自然語言處理深度學習方法中的“瑞士軍刀”。?
RNN特別依賴于順序/時間:它們按順序處理其輸入序列的時間步長,改組或反轉時間步長可以完全改變RNN從序列中提取的特征表示。雙向RNN利用RNN的順序敏感性:使用兩個常規(guī)RNN,例如GRU和LSTM層,每個層在一個方向上處理輸入序列(按時間順序和反時間順序),然后合并它們的特征表示。通過雙向處理序列,雙向RNN可以捕獲單向RNN可能忽略的特征模式。

值得注意的是,RNN層按時間順序處理序列(較早的時間步長)可能是一個隨意的假設。至少,這是迄今為止沒有試圖提出質疑的決定。如果按照反時間順序處理輸入序列,RNN的表現是否足夠好?但在自然語言處理中,理解句子中一個單詞的意思并不依賴于在句子中的位置。在反向IMDB數據集上使用LSTM。?
準備數據

from keras.datasets import imdb from keras.preprocessing import sequence from keras import layers from keras.models import Sequentialmax_features = 10000 maxlen = 500(x_train,y_train),(x_test,y_test)=imdb.load_data(num_words=max_features)x_train = [x[::-1] for x in x_train]#翻轉順序 x_test = [x[::-1] for x in x_test]x_train = sequence.pad_sequences(x_train, maxlen=maxlen) x_test = sequence.pad_sequences(x_test, maxlen=maxlen)model = Sequential()model.add(layers.Embedding(max_features, 128)) model.add(layers.LSTM(32)) model.add(layers.Dense(1, activation='sigmoid')) model.compile(optimizer='rmsprop',loss='binary_crossentropy',metrics=['acc']) history = model.fit(x_train, y_train,epochs=10,batch_size=128,validation_split=0.2)
  • ?

獲得的性能幾乎與按時間順序的LSTM相同。值得注意的是,在這樣的文本數據集中,逆序處理與時間順序處理一樣有效,證實了這樣的假設:雖然詞序在理解語言方面很重要,但使用的順序并不重要。重要的是,在逆向序列上訓練的RNN將比在原始序列上訓練的RNN學習不同的特征表現形式。在機器學習中,不同但有用的表示總是值得利用,它們越不同越好:它們提供了一個新的查看數據的角度,捕獲其他方法遺漏的數據的各個方面,可以幫助提高任務的性能。?
雙向RNN利用這一想法來改進按時間順序的RNN的性能。它以兩種方式查看其輸入序列,獲得可能更豐富的表示,并捕獲僅由時間順序版本遺漏的特征模式。?
?
Keras中實現雙向RNN需要使用Bidirectional網絡層,接受一個循環(huán)網絡層作為參數。Bidirectional網絡層生成第二個相同的循環(huán)網絡,其中一個網絡層用來處理順序輸入數據,另一個處理逆序輸入數據。?
雙向RNN訓練

model = Sequential()model.add(layers.Embedding(max_features, 32)) model.add(layers.Bidirectional(layers.LSTM(32))) model.add(layers.Dense(1, activation='sigmoid'))model.compile(optimizer='rmsprop',loss='binary_crossentropy',metrics= ['acc']) history = model.fit(x_train, y_train,epochs=10,batch_size=128,validation_split=0.2)
  • ?

表現比單向LSTM好一點。模型很快過擬合,雙向參數是單向LSTM的兩倍。

深度學習更像是一門藝術,而不是一門科學。可以提供指導,說明在特定問題上可能起作用或不合作的內容,但最終,每個問題都是獨一無二的;你必須根據經驗評估不同的策略。目前還沒有任何理論可以提前告訴你應該采取哪些措施來最佳地解決問題。你必須迭代

小結

  • 在處理新問題時,最好先為選擇的度量標準建立常識baseline。如果沒有baseline可以擊敗,無法判斷是否正在取得實際進展。[baseline參考物]
  • 在復雜模型之前嘗試簡單的模型,以證明額外的消耗。有時一個簡單模型將成為最佳選擇。
  • 當處理時序問題的數據時,循環(huán)網絡非常適合。
  • 要將dropout與循環(huán)網絡一起使用,應該使用時間常數drpoout mask和循環(huán)dropout mask。這些內置于Keras循環(huán)網絡層中,因此所要做的就是使用循環(huán)網絡層的dropout和recurrent_dropout參數。
  • 堆疊的RNN提供比單個RNN層更多的特征表示能力。但需要的計算能力也大大增加,并不值得。雖然他們在復雜問題(例如機器翻譯)上效果更好,它們可能并不總是與更小,更簡單的問題相關。
  • 雙向RNN,它以兩種方式查看序列,對自然語言處理問題很有用。當最近的過去數據比序列的開始數據提供更多信息時,表現情況并不理想。

卷積網絡處理序列數據

1D卷積網絡可以在某些序列處理問題上與RNN競爭,通常計算成本很低。最近,通常與擴張內核一起使用的1D convnets已經成功用于音頻生成和機器翻譯上。除了這些特定的成功之外,人們早就知道小型1D卷積網絡可以為RNN提供快速替代方案,用于簡單的任務,例如文本分類和時間序列預測。

序列數據上的1D卷積

2D卷積在每個小patch上進行卷積操作,和2D卷積類似,1D卷積在局部1D Patch(連續(xù)子序列)上進行卷積操作。?

這樣的一維卷積可以識別序列中的局部特征模式。因為對每個patch執(zhí)行相同的輸入變換,所以在句子中的某個位置處學習的模式稍后可以在不同的位置被識別,使得1D卷積平移不變(對于時間轉換)。?例如,使用大小為5的卷積窗口的1D卷積處理字符序列應該能夠學習長度為5或更小的單詞或單詞片段,并且它應該能夠在輸入序列的任何上下文中識別這些單詞。

序列數據的1D池化?
2D池化操作具有1D等效形式:從輸入提取1D patch(子序列)并輸出最大值(最大池化)或平均值(平均池化)。與2D convnets一樣,這用于減少1D輸入(子采樣)的長度。

實現一維卷積?
Keras中使用Conv1D網絡層[和Conv2D網絡層類似]。接收3D張量,形狀(samples,time,features),返回相同形狀的3D張量。卷積窗口是時間周上的1D卷口,輸入張量的axis1。?
使用Conv1D處理IMDB數據集?
數據處理

from keras.datasets import imdb from keras.preprocessing import sequencemax_features = 10000 max_len = 500 print('Loading data...') (x_train, y_train),(x_test, y_test)=imdb.load_data(num_words=max_features) print(len(x_train), 'train sequences') print(len(x_test), 'test sequences')print('Pad sequences (samples x time)') x_train = sequence.pad_sequences(x_train, maxlen=max_len) x_test = sequence.pad_sequences(x_test, maxlen=max_len) print('x_train shape:', x_train.shape) print('x_test shape:', x_test.shape)
  • 1?

1D convnets的結構與2D對應方式相同:它們由一堆Conv1D和MaxPooling1D層組成,以全局池層或Flatten層結束[將3D輸出轉換為2D輸出],允許將一個或多個Dense層添加到模型中以進行分類或回歸。?
一個不同之處在于,可以負擔得起使用帶有1D convnets的更大卷積窗口。對于2D卷積層,3×3卷積窗口包含3×3 = 9個特征向量;但是對于1D卷積層,大小為3的卷積窗口僅包含3個特征向量。因此,可以輕松使用尺寸為7或9的1D卷積窗口。?
IMDB 1D卷積

from keras.models import Sequential from keras import layers from keras.optimizers import RMSpropmodel = Sequential() model.add(layers.Embedding(max_features, 128, input_length=max_len)) model.add(layers.Conv1D(32, 7, activation='relu')) model.add(layers.MaxPooling1D(5)) model.add(layers.Conv1D(32, 7, activation='relu')) model.add(layers.GlobalMaxPooling1D()) model.add(layers.Dense(1))model.compile(optimizer=RMSprop(lr=1e-4),loss='binary_crossentropy',metrics=['acc']) history = model.fit(x_train, y_train,epochs=10,batch_size=128,validation_split=0.2)
  • ?

驗證準確性略低于LSTM,但CPU和GPU上的運行時間更快(速度與確切配置有很大關系)。此時,可以重新訓練此模型,epochs(8個)并在測試集上運行。這是一個令人信服的證明:一維卷積可以在字級情感分類任務中為循環(huán)網絡提供快速,廉價的替代方案。

使用CNN和RNN處理長序列數據

由于1D convnets獨立處理輸入patch,因此它們對時間步長的順序不敏感,這與RNN不同。當然,為了識別長期模式,可以堆疊許多卷積層和池化層,從而上層將看到原始輸入的長塊 - 但這仍然是誘導順序敏感性的相當弱的方式。證明這一弱點的一種方法是在溫度預測問題上嘗試一維卷積,其中順序敏感性是產生良好預測的關鍵。?
天氣數據上的1D卷積訓練

from keras.models import Sequential from keras import layers from keras.optimizers import RMSpropmodel = Sequential() model.add(layers.Conv1D(32, 5, activation='relu',input_shape=(None, float_data.shape[-1]))) model.add(layers.MaxPooling1D(3)) model.add(layers.Conv1D(32, 5, activation='relu')) model.add(layers.MaxPooling1D(3)) model.add(layers.Conv1D(32, 5, activation='relu')) model.add(layers.GlobalMaxPooling1D()) model.add(layers.Dense(1))model.compile(optimizer=RMSprop(), loss='mae') history = model.fit_generator(train_gen,steps_per_epoch=500,epochs=20,validation_data=val_gen,validation_steps=val_steps)
  • ?

訓練集、驗證集上損失值變化:?
?
驗證集上的MAE損失在0.4左右,比卷積baseline還差。這是因為convnet在輸入時間序列中的所有地方查找模式,并且不知道它看到的模式的時間位置(朝向開頭,朝向末尾,等等)。因為在這個特定預測問題的情況下,更新近的數據點應該與舊數據點的重要性不同,所以convnet無法產生有意義的結果。而IMDB數據,與正面或負面情緒相關聯(lián)的關鍵字模式是獨立于在輸入句子中找到它們的位置的信息。

將convnet的速度和優(yōu)點與RNN的順序敏感性相結合的一種策略是使用1D convnet作為RNN之前的預處理步驟。當你處理特別長時間無法用RNN實際處理的序列時,這種方法是特別有用的,例如具有數千步的序列數據。convnet會將長輸入序列轉換為更短(下采樣)的更高級別特征序列。抽取出來的特征序列作為RNN的輸入數據。?
?
在時間序列數據集上使用這種方法實驗。?
數據準備

step = 3 lookback = 720 delay = 144train_gen = generator(float_data,lookback=lookback,delay=delay,min_index=0,max_index=200000,shuffle=True,step=step) val_gen = generator(float_data,lookback=lookback,delay=delay,min_index=200001,max_index=300000,step=step) test_gen = generator(float_data,lookback=lookback,delay=delay,min_index=300001,max_index=None,step=step)val_steps = (300000 - 200001 - lookback) // 128 test_steps = (len(float_data) - 300001 - lookback) // 128
  • ?

1D卷積+GRU網絡層

from keras.models import Sequential from keras import layers from keras.optimizers import RMSPropmodel = Sequential() model.add(layers.Conv1D(32,5,activation='relu',input_shape= (None,float_data.shape[-1]))) model.add(layers.MaxPooling1D(3)) model.add(layers.Conv1D(32, 5, activation='relu')) model.add(layers.GRU(32, dropout=0.1, recurrent_dropout=0.5)) model.add(layers.Dense(1))model.compile(optimizer=RMSprop(), loss='mae') history = model.fit_generator(train_gen,steps_per_epoch=500,epochs=20,validation_data=val_gen,validation_steps=val_steps)
  • ?

?
從驗證集損失值結果來看,這種設置方式沒有單獨使用GRU網絡好,但運行速度更快。數據集是原來的兩倍,在這種情況下似乎沒有太大的幫助,但對其他數據集可能很重要。

小結

  • 通常,1D convnets的結構非常類似于計算機視覺領域的2D卷積網絡層:它們由Conv1D層和MaxPooling1D層組成,以全局池化操作或展平操作結束。
  • 由于RNN對于處理非常長的序列消耗非常昂貴,但是1D convnets相對較少,因此在RNN之前使用1D convnet作為預處理步驟,縮短序列并提取RNN處理的有用特征表示可能是個好主意

#

  • 可以將RNN用于時間序列回歸(“預測未來”),時間序列分類,時間序列中的異常檢測以及序列標記(例如識別句子中的名稱或日期);
  • 可以使用1D convnets進行機器翻譯(序列到序列卷積模型,如SliceNet),文檔分類和拼寫糾正;
  • 如果全局順序對序列數據很重要,那么最好使用循環(huán)網絡來處理它。時間序列通常就是這種情況,其中最近的過去可能比遙遠的過去更具信息性。
  • 如果全局順序沒有根本意義,那么1D convnets同樣有效并且消耗更低。這通常是文本數據的情況,其中在句子開頭找到的關鍵字與在結尾處找到的關鍵字一樣有意義。

總結

以上是生活随笔為你收集整理的Deep-Learning-with-Python] 文本序列中的深度学习的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。