2.keras實現-->深度學習用於文本和序列

1.將文本數據預處理爲有用的數據表示

  • 將文本分割成單詞(token),並將每個單詞轉換爲一個向量
  • 將文本分割成單字符(token),並將每個字符轉換爲一個向量
  • 提取單詞或字符的n-gram(token),並將每一個n-gram轉換爲一個向量。n-gram是多個連續單詞或字符的集合

 

將向量與標記相關聯的方法有:one-hot編碼與標記嵌入(token embedding)html

具體見https://www.cnblogs.com/nxf-rabbit75/p/9970320.htmlpython

 

2.使用循環神經網絡

(1)simpleRNN

simpleRNN能夠在兩種不一樣的模式下運行,這兩種模式由return_sequences這個構造函數參數來控制數組

一種是返回每一個時間步連續輸出的完整序列,
其形狀爲(batch_size,timesteps,output_features);
另外一種是隻返回每一個輸入序列的最終輸出,其形狀爲(batch_size,output_features)
# 例子1
from keras.models import Sequential
from keras.layers import Embedding,SimpleRNN
model = Sequential()
model.add(Embedding(10000,32)) #(batch_size,output_features)
model.add(SimpleRNN(32))
model.summary()

 

 
#例子2:RNN返回完整的狀態序列
model = Sequential()
model.add(Embedding(10000,32))
model.add(SimpleRNN(32,return_sequences = True)) #(batch_size,timesteps,output_features)
model.summary()

  

 

 

 

 

 咱們將這個模型應用IMDB電影評論分類問題網絡

#準備imdb數據
from keras.datasets import imdb
from keras.preprocessing import sequence

max_features = 10000# 做爲特徵的單詞個數最多10000,多了不算
max_len = 500 # 在這麼多單詞以後截斷文本
batch_size = 32

(input_train,y_train),(input_test,y_test) = imdb.load_data(num_words = max_features)
print(len(input_train),'train sequences')
print(len(input_test),'test sequences')

input_train = sequence.pad_sequences(input_train,maxlen=max_len)
input_test = sequence.pad_sequences(input_test,maxlen=max_len)
print(input_train.shape)
print(input_test.shape)

 

首先對數據進行預處理架構

25000 train sequences
25000 test sequences
(25000, 500)
(25000, 500)
 
#定義模型
from keras.models import Sequential
from keras.layers import Embedding,Flatten,Dense

model = Sequential()

model.add(Embedding(max_features,32))
model.add(SimpleRNN(32))
model.add(Dense(1,activation='sigmoid'))

model.summary()

model.compile(loss='binary_crossentropy',optimizer='rmsprop',metrics=['acc'])
history = model.fit(input_train,y_train,
                   epochs = 10,
                   batch_size = 128,
                   validation_split = 0.2)

 

 

 

 
import matplotlib.pyplot as plt

acc = history.history['acc']
loss = history.history['loss']
val_acc = history.history['val_acc']
val_loss = history.history['val_loss']

epochs = range(1,len(acc)+1)

plt.plot(epochs,acc,'bo',label='Training acc')
plt.plot(epochs,val_acc,'b',label='Validation acc')
plt.title('Traning and validation acc')
plt.legend()

plt.figure()

plt.plot(epochs,loss,'bo',label='Training loss')
plt.plot(epochs,val_loss,'b',label='Validation loss')
plt.title('Traning and validation loss')
plt.legend()

plt.show()

 

 

 

 

 

總結:app

這個小型循環網絡的表現並很差,問題的部分緣由在於: 輸入只考慮了前500個單詞,而不是整個序列,所以RNN得到的信息比前面的基準模型更少。dom

另外一部分緣由在於,simpleRNN不擅長處理長序列,好比文本。 其餘類型的循環層的表現要好得多,好比說LSTM層和GRU層機器學習

理論上來講,SimpleRNN應該可以記住許多時間步以前見過的信息,但實際上他是不可能學到這種長期以來的,緣由在於梯度消失問題,函數

這一效應相似於在層數較多的非循環網絡(即前饋網絡),隨着層數的增長,網絡最終變得沒法訓練。 LSTM和GRU都是爲了解決這個問題而設計的性能

 

 (2)LSTM

# keras實現LSTM的一個例子
from keras.layers import LSTM

model = Sequential()
model.add(Embedding(max_features,32))
model.add(LSTM(32))
model.add(Dense(1,activation='sigmoid'))

model.compile(optimizer='rmsprop',loss='binary_crossentropy',metrics=['acc'])
history = model.fit(input_train,y_train,
                   epochs = 10,
                   batch_size = 128,
                   validation_split = 0.2)
 
 

此次,驗證精度達到了,比simpleRNN好多了,主要是由於LSTM受梯度消失問題的影響要小得多,這個結果也比第三章的全鏈接網絡略好,

雖然使用的數據量比第三章少。此處在500個時間步以後將序列截斷,而在第三章是讀取整個序列

 

 

  循環神經網絡的高級用法
  • 循環dropout (下降過擬合)
  • 堆疊循環層 (提升網絡的表示能力(代價是更高的計算負荷))
  • 雙向循環層 (將相同的信息以不一樣的方式呈現給循環網絡,能夠提升精度並緩解遺忘問題)

 

咱們將使用一個天氣時間序列數據集-->德國耶拿的馬克思-普朗克生物地球化學研究所的氣象站記錄

溫度預測問題

數據集:每十分鐘記錄14個不一樣的量(好比氣溫、氣壓、溼度、風向等),原始數據可追溯到2003年。本例僅適用2009-2016年的數據,

這個數據集很是適合用來學習處理數值型時間序列咱們將會用這個數據集來構建模型,

輸入最近的一些數據(幾天的數據點),能夠預測24小時以後的氣溫。

 

import os 
data_dir = 'jena_climate'
fname = os.path.join(data_dir,'jena_climate_2009_2016.csv')

f = open(fname)
data = f.read()#全部行組成一個字符串,每行由\n隔開
f.close()

lines = data.split('\n') 
header = lines[0].split(',')
lines = lines[1:]

print(header) #時間步+14個與天氣有關的值
print(lines[0]) 
print(len(lines))#有420551行數據

 

日期+14個特徵名稱

一行的數據爲:

 
import numpy as np

float_data = np.zeros((len(lines),len(header)-1))
for i,line in enumerate(lines):
    values = [float(x) for x in line.split(',') [1:]]
    float_data[i,:] = values
    
float_data[1,:]

 

 

 

 
#繪製全部天的溫度變化
import matplotlib.pyplot as plt
plt.plot(range(len(lines)),float_data[:,1])
plt.show()

  

 

 

 
#繪製前十天的溫度時間序列
plt.plot(range(1440),float_data[:,1][:1440]) plt.show()

  

 

 

 上面這張圖,能夠看到天天的週期性變化,尤爲是最後4天特別明顯。 若是想根據過去幾個月的數據來預測下個月的平均溫度,那麼問題很簡單,

由於數據具備可靠的年度週期性。 從這幾天的數據來看,溫度看起來更混亂一些。以天做爲觀察尺度,這個時間序列是能夠預測的嗎? 

 
 準備數據
lookback = 720:給定過去5天內的觀測數據
steps = 6:每6個時間步採樣一次數據
delay = 144:目標是將來24小時以後的數據

 float_data

 

 
#數據標準化
float_data -= float_data[:20000].mean(axis=0)
float_data /= float_data[:20000].std(axis=0)
 
 
#生成時間序列樣本及其目標的生成器
def generator(data,lookback,delay,min_index,max_index,shuffle=False,batch_size=128,step=6):
    '''輸入:data是浮點數組成的原始數據
        lookback是輸入數據應該包括多少個時間步
        delay是目標應該在將來多少個時間步以後
        min_index,max_index是data數組中的索引,用於界定須要抽取哪些時間步(有助於保存一部分數據用於驗證,
        另外一部分用於測試)
        step=6 一個小時抽取一個數據點
   輸出:samples:輸入數據的一個批量,
        targets:對應的目標溫度數組'''
    if max_index is None:
        max_index = len(data) - delay + 1
    i = min_index + lookback
    while 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 + lookback
            rows = (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):
            indices = range(rows[j]-lookback,rows[j],step)
            samples[j] = data[indices]
            targets[j] = data[rows[j] + delay][1]
        
        yield samples,targets
 
 
#準備訓練生成器,驗證生成器,測試生成器
lookback =1440
step = 6
delay = 144
batch_size = 128

train_gen = generator(float_data,lookback=lookback,delay=delay,
                      min_index=0,max_index=200000,shuffle=True,batch_size=batch_size,step=step)
val_gen = generator(float_data,lookback=lookback,delay=delay,
                      min_index=200001,max_index=300000,shuffle=True,batch_size=batch_size,step=step)
test_gen = generator(float_data,lookback=lookback,delay=delay,
                      min_index=300001,max_index=None,shuffle=True,batch_size=batch_size,step=step)
 
#爲了查看整個訓練集,須要從train_gen中抽取多少次
train_steps = (200000-0-lookback) // batch_size
#爲了查看整個驗證集,須要從val_gen中抽取多少次
val_steps = (300000-200001-lookback) // batch_size
#爲了查看整個測試集,須要從val_gen中抽取多少次
test_steps = (len(float_data)-300001-lookback) // batch_size 
print(train_steps)
print(val_steps) 
print(test_steps)

1551

769

930

# 一種基於常識的、非機器學習的基準方法-->始終預測24小時後的溫度等於如今的溫度
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)


evaluate_naive_method ()

# 將MAE轉換成攝氏溫度偏差
std = float_data[:200000].std (axis=0)
celsius_mae = 0.29 * std[1]
# celsius_mae
 
from keras.models import Sequential
from keras import layers

#對比1.全鏈接網絡
model = Sequential()
model.add(layers.Flatten(input_shape=(lookback//step,float_data.shape[-1])))
model.add(layers.Dense(32,activation='relu'))
model.add(layers.Dense(1))

model.compile(optimizer='RMSprop',loss='mae')

history = model.fit_generator(
    train_gen,
    steps_per_epoch=50,
    epochs=10,
    validation_data=val_gen,
    validation_steps=val_steps
)

 

 

總結

第一個全鏈接方法的效果並很差,但這並不意味機器學習不適用於這個問題,該方法首先將時間序列展平,

這從輸入數據中刪除了時間的概念。 數據自己是個序列,其中因果關係和順序都很重要。

 
#對比2.循環網絡GRU
#訓練並評估一個基於GRU的模型
from keras.models import Sequential
from keras.layers import Dense,GRU

model = Sequential()
model.add(GRU(32,input_shape=(None,float_data.shape[-1])))
model.add(Dense(1))

model.compile(optimizer='rmsprop',loss='mae')
history = model.fit_generator(train_gen,
                             steps_per_epoch=50,
                             epochs=5,
                             validation_data = val_gen,
                             validation_steps = val_steps)

# loss: 0.3403 - val_loss: 0.3160

 

 

 

 
#使用dropout來下降過擬合
from keras.models import Sequential
from keras.layers import Dense,GRU

model = Sequential()
model.add(GRU(32,dropout=0.2,recurrent_dropout=0.2,input_shape=(None,float_data.shape[-1])))
model.add(Dense(1))

model.compile(optimizer='RMSprop',loss='mae')
history = model.fit_generator(train_gen,
                             steps_per_epoch=50,
                             epochs=5,
                             validation_data = val_gen,
                             validation_steps = val_steps)

 

  • dropout:0~1之間的浮點數,控制輸入線性變換的神經元斷開比例

  • recurrent_dropout:0~1之間的浮點數,控制循環狀態的線性變換的神經元斷開比例

 

 

 

模型再也不過擬合,但彷佛遇到了性能瓶頸,因此咱們應該考慮增長網絡容量

增長網絡容量直到過擬合變成主要的障礙。

只要過擬合不是太嚴重,那麼極可能是容量不足的問題

 
 
#對比3.循環層堆疊
#訓練並評估一個使用dropout正則化的堆疊GRU模型
#數據集:溫度
from keras.models import Sequential
from keras.layers import Dense,GRU

model = Sequential()
model.add(GRU(32,dropout=0.1,recurrent_dropout=0.5,input_shape=(None,float_data.shape[-1]),return_sequences=True))
# return_sequences=True,在keras中逐個堆疊循環層,全部中間層都應該返回完整的輸出序列,
# 而不是隻返回最後一個時間步的輸出
model.add(GRU(64,activation='relu',dropout=0.1,recurrent_dropout=0.5))
# 可是最後一個LSTM層return_sequences一般爲false
model.add(Dense(1))

model.summary()
model.compile(optimizer='rmsprop',loss='mae')
history = model.fit_generator(train_gen,
                             steps_per_epoch=50,
                             epochs=5,
                             validation_data = val_gen,
                             validation_steps = val_steps)

 

 

 

 

 

 

結果如上圖所示,能夠看出,添加一層的確對結果有所改進,但並不顯著,

咱們能夠獲得兩個結論:

1.由於過擬合仍然不是很嚴重,因此能夠放心的增大每層的大小,

以進一步改進驗證損失,但這麼作的計算成本很高

2.添加一層厚模型並無顯著改進,因此你可能發現,提升網絡能力

的回報在逐漸減少

 
#對比4:逆序序列評估LSTM
#數據集 imdb from keras.datasets import imdb from keras.preprocessing import sequence from keras import layers from keras.models import Sequential max_features = 10000 max_len = 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 = max_len) x_test = sequence.pad_sequences(x_test,maxlen = max_len) model = Sequential() model.add(layers.Embedding(max_features,128)) model.add(layers.LSTM(32)) #return_sequences=True model.add(Dense(1,activation='sigmoid')) model.summary() model.compile(optimizer='rmsprop',loss='binary_crossentropy',metrics = ['acc']) history = model.fit(x_train,y_train, epochs=2, batch_size=128, validation_split=0.2) #沒有分val出來,直接寫0.2就表示從train數據集中分0.2出來當作驗證集

 

 

 

總結:模型性能與正序LSTM幾乎相同,這證明了一個假設,雖然單詞順序對理解語言很重要,但使用哪一種順序不重要。

雙向RNN正是利用這個想法來提升正序RNN的性能,他從兩個方向查看數據,從而獲得更加豐富的表示,並捕捉到僅使用正序RNN時可能忽略的一些模式

 

(3)Bidirectional RNN

#訓練並評估一個雙向LSTM
#數據集imdb
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 = 5,
                   batch_size = 128,
                   validation_split = 0.2)
 
#訓練一個雙向GRU
#數據集 溫度
model = Sequential()
model.add(layers.Bidirectional(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,
                   epochs = 5,
                   steps_per_epoch = 50,
                   validation_data = val_gen,
                   validation_steps = val_steps)

 

 
  更多嘗試
  • 在堆疊循環層中調節每層的單元個數,當前取值在很大程度上是任意選擇的,所以可能不是最優的。
  • 調節rmsprop優化器的學習率
  • 嘗試使用LSTM層代替GRU
  • 在循環層上面嘗試使用更大的密集鏈接迴歸器,即更大的Dense層或Dense層的堆疊
  • 在測試集上運行性能最佳的模型,不然,開發的網絡架構將會對驗證集過擬合
 


3.使用一維卷積神經網絡處理序列

## 用卷積神經網絡處理序列

## 實現一維卷積神經網絡
#準備imdb數據
from keras.datasets import imdb
from keras.preprocessing import sequence
from keras import layers
from keras.models import Sequential

max_features = 10000
max_len = 500

(x_train,y_train),(x_test,y_test) = imdb.load_data(num_words = max_features)
print('x_train序列長度',len(x_train))
print('x_test序列長度',len(x_test))

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)

x_train序列長度 25000
x_test序列長度 25000
x_train shape (25000, 500)
x_test shape (25000, 500)
#imdb數據上訓練並評估一個簡單的一維卷積神經網絡
from keras.models import Sequential
from keras import layers
from keras.optimizers import RMSprop

model = 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())  #GlobalMaxPooling1D和GlobalMaxPool1D有啥關係?
model.add(layers.Dense(1))

model.summary()

model.compile(optimizer=RMSprop(lr=1e-4),
             loss = 'binary_crossentropy',
             metrics = ['acc'])
history = model.fit(x_train,y_train,
                   epochs = 2,
                   batch_size = 128,
                   validation_split = 0.2)

 

 

總結: 用一維卷積神經網絡驗證精度略低於LSTM,但在CPU和GPU上的運行速度都要更快

在單詞級的情感分類任務上,一維卷積神經網絡能夠替代循環網絡,而且速度更快、計算代價更低。

 

 

#耶拿數據上訓練並評估一個簡單的一維卷積神經網絡
from keras.models import Sequential
from keras import layers
from keras.optimizers import RMSprop

model = 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())  #GlobalMaxPooling1D和GlobalMaxPool1D有啥關係?
model.add(layers.Dense(1))

model.summary()

model.compile(optimizer=RMSprop(),loss = 'mae')
             
history = model.fit_generator(train_gen,
                   epochs = 5,
                   steps_per_epoch = 50,
                   validation_data = val_gen,
                   validation_steps = val_steps)

 驗證MAE停留在0.4-0.5,使用小型卷積神經網絡甚至沒法擊敗基於嘗試的基準方法。一樣,這是由於卷積神經網絡在輸入時間序列的全部位置尋找模式,它並不知道所看到某個模式的時間位置(距開始多長時間,距結束多長時間等)。對於這個具體的預測問題,對最新數據點的解釋與對較早數據點的解釋應該並不相同,因此卷積神經網絡沒法獲得有意義的結果。

 

卷積神經網絡對IMDB數據來講並非問題,由於對於正面情緒或負面情緒相關聯的關鍵詞模式,不管出如今輸入句子中的什麼位置,它所包含的信息量是同樣的

要想結合卷積神經網絡的速度與輕量 與 RNN的順序敏感性,一種方法是在RNN前面使用一維神經網絡做爲預處理步驟。

對於那些很是長,以致於RNN沒法處理的序列,這種方法尤爲有用。卷積神經網絡能夠將長的輸入序列轉換爲高級特徵組成的更短序列(下采樣)

而後,提取的特徵組成的這些序列成爲網絡中RNN的輸入。

 
 

在RNN前面使用一維卷積神經網絡做爲預處理步驟

長序列-->經過一維CNN-->更短的序列(CNN特徵)->經過RNN-->輸出

這裏將step減半,獲得時間序列的長度變爲以前的兩倍,溫度數據的採樣頻率爲每30分鐘一個數據點

 

 

 

 
#準備訓練生成器,驗證生成器,測試生成器
lookback =720
step = 3
delay = 144
batch_size = 128

train_gen = generator(float_data,lookback=lookback,delay=delay,
                      min_index=0,max_index=200000,shuffle=True,batch_size=batch_size,step=step)
val_gen = generator(float_data,lookback=lookback,delay=delay,
                      min_index=200001,max_index=300000,shuffle=True,batch_size=batch_size,step=step)
test_gen = generator(float_data,lookback=lookback,delay=delay,
                      min_index=300001,max_index=None,shuffle=True,batch_size=batch_size,step=step)


#爲了查看整個訓練集,須要從train_gen中抽取多少次
train_steps = (200000-0-lookback) // batch_size
#爲了查看整個驗證集,須要從val_gen中抽取多少次
val_steps = (300000-200001-lookback) // batch_size
#爲了查看整個測試集,須要從val_gen中抽取多少次
test_steps = (len(float_data)-300001-lookback) // batch_size 
print(train_steps)
print(val_steps) 
print(test_steps)

1556

775

936

 
#結合一維CNN和GRU來處理長序列
from keras.models import Sequential
from keras import layers
from keras.optimizers import RMSprop

model = 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.summary()

model.compile(optimizer=RMSprop(),loss = 'mae')
             
history = model.fit_generator(train_gen,
                   epochs = 5,
                   steps_per_epoch = 50,
                   validation_data = val_gen,
                   validation_steps = val_steps)
 

 從驗證損失來看,這種架構的效果不如只用正則化GPU,但速度要快不少,它查看了兩倍的數據量,在本例中可能不是很是有用,但對於其餘數據集可能很是重要

相關文章
相關標籤/搜索