目錄
前置知識
- 深度學習是什麼
- 深度學習是機器學習的一個分支
- 由全連接網絡、卷積神經網絡和循環神經網絡構成的結構
- 多層全連接網絡:多層感知器
- 多層卷積神經網絡
- 卷積神經網絡基本結構
- 數據:
- 2D輸入數據形式:[批尺寸(batchsize),高度(H),寬度(W),通道數(特徵數)(channel)],[B, H, W, C]
- 2D卷積核心格式:[(卷積核心大小1,卷積核心大小2),輸入通道數(特徵),輸出通道數(特徵)]
- 1D輸入數據形式:[B, T, C]
- 1D卷積核心格式:[K, C, C2]
- 2D數據:圖像、雷達等
- 1D數據:有順序的文本、信號
- 有些人感覺鳶尾花數據是1D數據,其僅是一個向量點
- 卷積:提取特徵,也就是濾波
- 池化:降採樣(stride 1)
- LeNet(手寫數字識別):卷積+全連接
- 卷積神經網絡中最重要的概念是什麼
- 感受野(由卷積的kernel size決定)
- 如何增加感受野
- 增加層數
- 降採樣
- 數據:
- 深度學習中重要的觀念:
- 向量:深度學習中所有的屬性、物體、特徵均是向量(大部分網絡均可由Numpy實現)
循環神經網絡(RNN)
RNN核心思想:可以考慮輸入數據前後文相關特徵
文本向量化
- 常規文本向量化(不考慮順序)
- 將整片文章轉換爲一個向量
- 基於詞頻統計的
- 詞袋子(Bag of words)模型
- 此時丟失了順序的信息
- eg:今 天 天 氣 不 好
- 看到“好”字我們應該認爲是一個正面的情緒
- 但是“好”前有個“不”
- 有些人提取前後文特徵組成“詞”->N-gtam-range
- 單個字或詞帶有信息少,需要結合前後文
- 對順序文本進行向量化
- 句子向量化:今 天 天 氣 不 好 。
- 需要將字轉換爲ID:建立字典,爲每個字符賦予一個整形數字
- 句子轉換爲:[96, 22, 22, 163, 3, 244, 1] -> [T], type:int
- 對每個整形數字進行OneHot編碼:[T=字符數量,字符數量]
- 可能有多個(BatchSize,B)句子,因此
- 輸入:[B, T, 字符數量]->[B, T, 字符數量]->一維連續數據
- 每個句子長度不同->補0->計算效率
- 深度神經網絡僅能處理浮點型的向量,所以將每個字符均轉換爲向量
- 但是字符數量長度太長,需要降維,乘以降維矩陣W[詞數量,降維長度]
- 最終:[B, T , 降維長度]
- 注意:
- W初始取隨機值,隨後隨網絡一同訓練
- X:[B, T, C] W[C, C2],如何相乘,張量點乘
- 整個過程叫做Embedding
- 將文字轉換爲向量的過程
- 中文以“字”作爲基本單位,英文可以以“字母”或“詞”作爲基本單位
RNN 建模
- 假設X是Embedding後的序列:[B, T, C]
- 選取某個時間X[: , 0, :], 相當於取第1個詞[B, C]
- 記錄-> , 所以X[:, 0, :]是
- 如果構建線性模型:,實際上金相當於處理單個字符
- 如果需要考慮前文內容,需要進行如下改動:
- 輸入:
- 狀態state: 其中默認爲0
- 輸出:
- 因此有:
- 其中的形式爲[batchsize, features1],的形式爲[batchsize, features2]。多層rnn網絡可以在輸出的基礎上繼續加入RNN函數:
其中表示隱藏層
Numpy實現(僅包含計算過程):
import numpy as np
# 讀取字典
word_map_file = open(r'model\wordmap','r',encoding='utf-8')
word2id_dict = eval(word_map_file.read())
word_map_file.close()
# print(word2id_dict)
# 文本向量化
B = 2 #文本數量(批次大小)
n_words = 5388
strs1 = '今天天氣不好。'
strs2 = '所以我不出門。'
strs_id1 = [word2id_dict.get(itr) for itr in strs1]
strs_id2 = [word2id_dict.get(itr) for itr in strs2]
print(f'文本1:“{strs1}”, 對應字典中的整數:{strs_id1}')
print(f'文本2:“{strs2}”, 對應字典中的整數:{strs_id2}')
# One hot編碼
T = max(len(strs1), len(strs2)) #字符串長度
C = len(word2id_dict) #字典中的字符數量,總數
strs_vect = np.zeros([B, T, C])
for idx, ids in enumerate(strs_id1):
strs_vect[0, idx, ids] = 1
for idx, ids in enumerate(strs_id2):
strs_vect[1, idx, ids] = 1
# print(strs_vect)
print(f'降維前 Size:{strs_vect.shape}')
# 降維: 向量文本乘上一個矩陣[字符數量:5388,降維的維度:128(可訓練)]
enbedding_size = 128
W = np.random.normal(0, 0.1, [n_words, enbedding_size])
vect2d = np.reshape(strs_vect, [B*T, C])
out = vect2d @ W
vect = np.reshape(out, [B, T, enbedding_size]) #Embedding 過程(壓縮矩陣)
print(f'降維後 Size:{vect.shape}')
###############
##### RNN #####
###############
#初始化
hidden_size = 64
rnn_w = np.random.random([enbedding_size+hidden_size, hidden_size])
rnn_b = np.zeros([hidden_size])
state = np.zeros([B, hidden_size])
#正向計算傳播
outputs = []
for step in range(T):
x_t = np.concatenate([vect[:, step, :], state], axis=1)
state = np.tanh(x_t @ rnn_w + rnn_b)
outputs.append(state)
last_output = outputs[-1] #包含前面全部信息
Tensorflow實現(僅框架,沒有傳入數據):
import tensorflow as tf
# 超參數
batch_size = 32 # B = 32
seq_len = 100 # 文本長度, T=100
embedding_size = 128 # 降維後向量長度 C = 128
hidden_size = 128 # 隱藏層向量長度
epochs = 100 # 訓練次數
# 統計量
n_words = 5388 # 字符數量
n_class = 10 # 類別數量
# 原始數據
input_ID = tf.placeholder(tf.int32, [batch_size, seq_len])
label_ID = tf.placeholder(tf.int32, [batch_size])
# 降維數據
embedding_w = tf.get_variable('embedding_w', [n_words, embedding_size])
# 傳入模型數據
inputs = tf.nn.embedding_lookup(embedding_w, input_ID)
# 構建多層神經網絡單元
rnn_fn = tf.nn.rnn_cell.BasicRNNCell
rnn_cell = tf.nn.rnn_cell.MultiRNNCell([
rnn_fn(hidden_size),
rnn_fn(hidden_size)
])
# 將數據輸入循環神經網絡
outputs, last_state = tf.nn.dynamic_rnn(rnn_cell, inputs, dtype = tf.float32)
last_out = outputs[:, 0, :]
logits = tf.layers.dense(last_out, n_class, activation= None)
label_onehot = tf.one_hot(label_ID, n_class)
loss = tf.losses.softmax_cross_entropy(label_onehot, logits)
train_step = tf.train.AdamOptimizer().minimize(loss)
sess = tf.Session()
sess.run(tf.global_variables_initializer())
for step in range(epochs):
sess.run(train_step, feed_dict={label_ID:..., input_ID:...})
RNN 模型改進
- RNN時間步較多時,梯度容易過大
- RNN使用的激活函數是tanh
- BasicRNN,容易出現“遺忘”
- 改進的LSTM結構,長短時間記憶單元
- 兩個向量用於保留記憶,h, c
- 門控結構是一個加權的機制,在很多網絡中都有體現
LSTM(Long Short Term Memory)
其中:爲sigmod函數,爲雙曲正切函數, 圖中左上角省略了輸入記憶單元,左上角省略了輸出記憶單元,左下角省略了輸入狀態單元
公式如下:
其中:
- 因爲,所以該部分的輸出皆作爲控制門信號
- 爲遺忘門信號(圖中最左側的),判斷當前信息以及前文信息是否重要
- 圖中中間的爲輸入門(),判斷短時記憶是否重要,將短時記憶加入長時記憶中
- 圖中中間的爲Cell輸入信號(),其中包含短時記憶以及當前信息
- 圖中右側的輸出門(),判斷當前長時記憶與短時記憶是否重要,是否輸出
- 爲長時間記憶
- 爲短時間記憶
LSTM變形與數學表達式
其中:
- 爲遺忘門,判斷長時記憶與短時記憶是否重要,是否需要遺忘
- 爲輸入門,判斷短時記憶是否重要,將短時記憶加入長時記憶中
- 爲輸出門,判斷當前長時記憶與短時記憶是否重要,是否輸出
}$爲長時間記憶 - 爲短時間記憶
LSTM變形與數學表達式
[外鏈圖片轉存中…(img-94hEYY9w-1593923703227)]
其中:
- 爲遺忘門,判斷長時記憶與短時記憶是否重要,是否需要遺忘
- 爲輸入門,判斷短時記憶是否重要,將短時記憶加入長時記憶中
- 爲輸出門,判斷當前長時記憶與短時記憶是否重要,是否輸出
- 爲Cell輸入信號,其中包含短時記憶以及當前信息
門控循環單元GRU(Grated Recurrent Unit)
GRU爲簡化版本的LSTM,理論上速度會有顯著提升,具體算法:GRU
雙向RNN模型
- 同時考慮“過去”和“未來”的信息
- 構建兩個RNN/LSTM傳播的單元(一個正向,一個反向)
模型構建代碼:
import tensorflow as tf
# 超參數
batch_size = 16
seq_len = 100 # 100個字
emb_size = 128 # 字符向量長度
n_layer = 2
n_hidden = 128
# 統計量
n_words = 5388 # 多少字符
n_class = 10 # 多少類
# 定義輸入:長度爲100的字符ID序列,類型INT
# 定義標籤:每個時間步均需要做分類
inputs = tf.placeholder(tf.int32, [batch_size, seq_len])
labels = tf.placeholder(tf.int32, [batch_size, seq_len])
mask = tf.placeholder(tf.float32, [batch_size, seq_len])
# Embedding
emb_w = tf.get_variable("emb_w", [n_words, emb_size]) #是可訓練的
inputs_emb = tf.nn.embedding_lookup(emb_w, inputs)
# inputs_emb是神經網絡輸入[batch_size(B), seq_len(T), emb_size(C)]
# 定義多層神經網絡
# cell_fn = tf.nn.rnn_cell.BasicRNNCell # 基本RNN
cell_fn = tf.nn.rnn_cell.LSTMCell # LSTM
# 向前傳播的單元
cell_fw = tf.nn.rnn_cell.MultiRNNCell(
[cell_fn(n_hidden) for itr in range(n_layer)])
# 反向傳播的單元
cell_bw = tf.nn.rnn_cell.MultiRNNCell(
[cell_fn(n_hidden) for itr in range(n_layer)])
# 將Embedding後的向量輸入循環神經網絡中
outputs, last_state = tf.nn.dynamic_rnn(cell_fw, inputs_emb, dtype=tf.float32)
intputs_bw = tf.reverse(inputs_emb, 1)
outputs_bw, last_state = tf.nn.dynamic_rnn(cell_bw, intputs_bw, dtype=tf.float32)
outputs_bw = tf.reverse(outputs_bw, 1)
# # 或者使用TF中tf.nn.bidirectional_dynamic_rnn()進行搭建,不許手動將輸入/輸出反向
# # 雙向RNN,seqlen用於不同長度序列解碼
# (fw_output, bw_output), state = tf.nn.bidirectional_dynamic_rnn(
# cell_fw_cell,
# cell_bw_cell,
# emb_input,
# seqlen,
# dtype=tf.float32
# )
# outputs = tf.concat([outputs, outputs_bw], 2)
# outputs相當於Y[batch_size, seq_len, n_hidden]
# logits,每個時間步需要預測類別
logits = tf.layers.dense(outputs, 4)
# 優化過程
loss = tf.contrib.seq2seq.sequence_loss(
logits, # 網絡的輸出
labels, # 標籤
mask # 掩碼 用於選取非補0區域數據,具體操作爲對補0區域乘上0權重
)
step = tf.train.AdamOptimizer().minimize(loss)