編者按:2個月前,DeepMind發表了一篇名為“神經算術邏輯單元(NALU)”的新論文,提出了一個能幫助神經網絡更好地模擬數值信息的新框架。這是一篇有趣的論文,解決的問題也很實際,所以今天論智想推薦一篇有關這個框架的文章,它也是被TensorFlow官博力薦的佳作。比起復雜的論文解讀,它更簡潔直觀,也易于理解。
現如今,盡管深度學習已經在許多任務中取得了令人驚艷的成果,諸多AI產品也逐漸在醫療等領域發揮越來越重要的作用,但如何教導神經網絡還是它的一個重要問題,說出來可能有人不信,神經網絡在簡單算術任務上還會出現問題。
在一個實驗中,DeepMind的研究人員曾訓練了一個精度接近完美的模型,它能從數據中找出范圍在-5到5之間的數字,但當輸入從未見過的新數據后,模型就無法概括了。
論文針對上述問題提出了兩種方法,但這里我們不會搬運原文的詳細內容,相反地,下文將簡要介紹NAC的工作原理,以及它如何處理加減乘除等操作,相應代碼也會在文章中列出,讀者可以從中獲得更直觀的了解。
第一個神經網絡(NAC)
論文介紹的第一個神經網絡是神經累積器(簡稱NAC),它能對輸入執行線性變換,而用于變換的矩陣是tanh(What)和sigmoid(Mhat)的元素乘積。簡而言之,input(x)后,模型輸入會乘以變換矩陣W,并產生輸出a。
NAC的Python實現:
# NAC
W_hat = tf.Variable(tf.truncated_normal(shape, stddev=0.02))
M_hat = tf.Variable(tf.truncated_normal(shape, stddev=0.02))
W = tf.tanh(W_hat) * tf.sigmoid(M_hat)
# 前向傳播
a = tf.matmul(in_dim, W)
第二個神經網絡(NALU)
神經算術邏輯單元(NALU)由兩個NAC構成,其中,第一個NAC g是sigmoid(Gx),第二個NAC在一個等于exp(W(log(|x| + epsilon)))的對數空間m中運行。
NALU的Python實現:
import tensorflow as tf
# NALU
G = tf.Variable(tf.truncated_normal(shape, stddev=0.02))
m = tf.exp(tf.matmul(tf.log(tf.abs(in_dim)+ epsilon), W))
g = tf.sigmoid(tf.matmul(in_dim, G))
y = g * a +(1- g)* m
通過加法理解NAC
現在我們來進行測試。首先,把NAC轉成函數:
# NAC
def NAC(in_dim, out_dim):
in_features = in_dim.shape[1]
# 定義W_hat和M_hat
W_hat = tf.get_variable(name = 'W_hat', initializer=tf.initializers.random_uniform(minval=-2, maxval=2),shape=[in_features, out_dim], trainable=True)
M_hat = tf.get_variable(name = 'M_hat', initializer=tf.initializers.random_uniform(minval=-2, maxval=2), shape=[in_features, out_dim], trainable=True)
W = tf.nn.tanh(W_hat) * tf.nn.sigmoid(M_hat)
a = tf.matmul(in_dim, W)
return a, W
其次,創建一些數據,把它們分成訓練集和測試集。NumPy有一個較numpy.arrange的API,很適合用來創建數據集:
# 生成一系列輸入數字X1和X2用于訓練
x1 = np.arange(0,10000,5, dtype=np.float32)
x2 = np.arange(5,10005,5, dtype=np.float32)
y_train = x1 + x2
x_train = np.column_stack((x1,x2))
print(x_train.shape)
print(y_train.shape)
# 生成一系列輸入數字X1和X2進行測試
x1 = np.arange(1000,2000,8, dtype=np.float32)
x2 = np.arange(1000,1500,4, dtype= np.float32)
x_test = np.column_stack((x1,x2))
y_test = x1 + x2
print()
print(x_test.shape)
print(y_test.shape)
接著,用這些準備好的東西訓練模型。我們先定義占位符X和Y以在運行時提供數據,用tf.reduce_sum()計算損失,模型包含兩個超參數:學習率alpha和訓練幾個epochs。在訓練開始前,我們還要定義一個優化器,方便用tf.train.AdamOptimizer()降低損失。
# 定義占位符以在運行時提供輸入
X = tf.placeholder(dtype=tf.float32, shape =[None , 2]) # Number of samples x Number of features (number of inputs to be added)
Y = tf.placeholder(dtype=tf.float32, shape=[None,])
#定義網絡
#這里網絡只包含一個NAC(用于測試)
y_pred, W = NAC(in_dim=X, out_dim=1)
y_pred = tf.squeeze(y_pred) # Remove extra dimensions if any
# 均方誤差 (MSE)
loss = tf.reduce_mean( (y_pred - Y) **2)
# 訓練參數
alpha = 0.05 # learning rate
epochs = 22000
optimize = tf.train.AdamOptimizer(learning_rate=alpha).minimize(loss)
with tf.Session() as sess:
#init = tf.global_variables_initializer()
cost_history = []
sess.run(tf.global_variables_initializer())
# 訓練前損失
print("Pre training MSE: ", sess.run (loss, feed_dict={X: x_test, Y:y_test}))
print()
for i in range(epochs):
_, cost = sess.run([optimize, loss ], feed_dict={X:x_train, Y: y_train})
print("epoch: {}, MSE: {}".format( i,cost) )
cost_history.append(cost)
# 列出每次迭代的均方誤差
plt.plot(np.arange(epochs),np.log(cost_history)) # Plot MSE on log scale
plt.xlabel("Epoch")
plt.ylabel("MSE")
plt.show()
print()
print(W.eval())
print()
# 訓練后損失
print("Post training MSE: ", sess.run(loss, feed_dict={X: x_test, Y: y_test}))
print("Actual sum: ", y_test[0:10])
print()
print("Predicted sum: ", sess.run(y_pred[0:10], feed_dict={X: x_test, Y: y_test}))
訓練完成后,我們可以得到這樣一幅圖損失曲線圖:
Actual sum: [2000.2012.2024.2036.2048.2060.2072.2084.2096.2108.]
Predicted sum: [1999.90212011.90152023.90092035.90042047.89972059.89922071.8984
2083.8982095.89752107.8967]
如輸出所示,NAC可以處理諸如加減法的操作,但它還做不到處理乘法和除法。為了解決這個問題,我們就要用到NALU。
通過乘法理解NALU
在上文基礎上,首先我們再添加一個NAC,組成NALU:
如果說NAC只是對輸入做線性變化,那么NALU就是把兩個具有權重的NAC組合在一起,用來執行加減(較小的紫色單元)和乘除(較大的紫色單元),計算由門(橙色單元)控制。
# NALU
def NALU(in_dim, out_dim):
shape = (int(in_dim.shape[-1]), out_dim)
epsilon = 1e-7
# NAC
W_hat = tf.Variable(tf.truncated_normal(shape, stddev=0.02))
M_hat = tf.Variable(tf.truncated_normal(shape, stddev=0.02))
G = tf.Variable(tf.truncated_normal(shape, stddev=0.02))
W = tf.tanh(W_hat) * tf.sigmoid(M_hat)
# 前向傳播
a = tf.matmul(in_dim, W)
# NALU
m = tf.exp(tf.matmul(tf.log(tf.abs(in_dim) + epsilon), W))
g = tf.sigmoid(tf.matmul(in_dim, G))
y = g * a + (1 - g) * m
return y
這里我們再創建一些數據,但和上次相比,這次要做一些改動:在第8行和第20行,我們把運算符從加改成了乘。
# 通過學習乘法來測試網絡
# 生成一系列輸入數字X1和X2用于訓練
x1 = np.arange(0,10000,5, dtype=np.float32)
x2 = np.arange(5,10005,5, dtype=np.float32)
y_train = x1 * x2
x_train = np.column_stack((x1,x2))
print(x_train.shape)
print(y_train.shape)
# 生成一系列輸入數字X1和X2進行測試
x1 = np.arange(1000,2000,8, dtype=np.float32)
x2 = np.arange(1000,1500,4, dtype= np.float32)
x_test = np.column_stack((x1,x2))
y_test = x1 * x2
print()
print(x_test.shape)
print(y_test.shape)
之后是訓練模型,需要注意的是,這里我們定義的還是NAC,而不是NALU:
# 定義占位符以在運行時提供值
X = tf.placeholder(dtype=tf.float32, shape =[None , 2]) # Number of samples x Number of features (number of inputs to be added)
Y = tf.placeholder(dtype=tf.float32, shape=[None,])
# 定義網絡
# 這里網絡只包含一個NAC(用于測試)
y_pred = NALU(in_dim=X, out_dim=1)
y_pred = tf.squeeze(y_pred) # Remove extra dimensions if any
# 均方誤差 (MSE)
loss = tf.reduce_mean( (y_pred - Y) **2)
# 訓練參數
alpha = 0.05 # 學習率
epochs = 22000
optimize = tf.train.AdamOptimizer(learning_rate=alpha).minimize(loss)
with tf.Session() as sess:
#init = tf.global_variables_initializer()
cost_history = []
sess.run(tf.global_variables_initializer())
# 訓練前損失
print("Pre training MSE: ", sess.run (loss, feed_dict={X: x_test, Y: y_test}))
print()
for i in range(epochs):
_, cost = sess.run([optimize, loss ], feed_dict={X: x_train, Y: y_train})
print("epoch: {}, MSE: {}".format( i,cost) )
cost_history.append(cost)
# 列出每次迭代的損失
plt.plot(np.arange(epochs),np.log(cost_history)) # Plot MSE on log scale
plt.xlabel("Epoch")
plt.ylabel("MSE")
plt.show()
# 訓練后損失
print("Post training MSE: ", sess.run(loss, feed_dict={X: x_test, Y: y_test}))
print("Actual product: ", y_test[0:10])
print()
print("Predicted product: ", sess.run(y_pred[0:10], feed_dict={X: x_test, Y: y_test}))
Actual product: [1000000.1012032.1024128.1036288.1048512.1060800.1073152.1085568.
1098048.1110592.]
Predicted product: [1000000.21012032. 1024127.561036288.61048512.061060800.8
1073151.61085567.61098047.61110592.8 ]
如果想獲取在TensorFlow中實現NALU的完整代碼,可以去這個github:github.com/ahylton19/simpleNALU-tf
小結
以上只是NALU在加減乘除任務上具體表現,在論文中,研究人員還測試了平方運算和開根,NALU的表現都優于傳統框架。簡而言之,DeepMind的這個簡單而實用的技術讓神經網絡掌握了數值推算,它類似傳統處理器中的算術邏輯單元,能讓網絡真正“學會”加減乘除和基于加減乘除的近似估計,更好地把經驗外推到其他數值任務上,而不再受訓練數據限制。
通過這篇文章,我們希望現在你已經了解了這篇轟動學界的論文到底說了什么,以及它對深度學習的貢獻和影響。
-
神經網絡
+關注
關注
42文章
4771瀏覽量
100723 -
數據
+關注
關注
8文章
7006瀏覽量
88958 -
NAC
+關注
關注
0文章
5瀏覽量
7701
原文標題:TensorFlow推薦:神經算術邏輯單元的直觀理解
文章出處:【微信號:jqr_AI,微信公眾號:論智】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論