編者按:2個(gè)月前,DeepMind發(fā)表了一篇名為“神經(jīng)算術(shù)邏輯單元(NALU)”的新論文,提出了一個(gè)能幫助神經(jīng)網(wǎng)絡(luò)更好地模擬數(shù)值信息的新框架。這是一篇有趣的論文,解決的問題也很實(shí)際,所以今天論智想推薦一篇有關(guān)這個(gè)框架的文章,它也是被TensorFlow官博力薦的佳作。比起復(fù)雜的論文解讀,它更簡(jiǎn)潔直觀,也易于理解。
現(xiàn)如今,盡管深度學(xué)習(xí)已經(jīng)在許多任務(wù)中取得了令人驚艷的成果,諸多AI產(chǎn)品也逐漸在醫(yī)療等領(lǐng)域發(fā)揮越來(lái)越重要的作用,但如何教導(dǎo)神經(jīng)網(wǎng)絡(luò)還是它的一個(gè)重要問題,說出來(lái)可能有人不信,神經(jīng)網(wǎng)絡(luò)在簡(jiǎn)單算術(shù)任務(wù)上還會(huì)出現(xiàn)問題。
在一個(gè)實(shí)驗(yàn)中,DeepMind的研究人員曾訓(xùn)練了一個(gè)精度接近完美的模型,它能從數(shù)據(jù)中找出范圍在-5到5之間的數(shù)字,但當(dāng)輸入從未見過的新數(shù)據(jù)后,模型就無(wú)法概括了。
論文針對(duì)上述問題提出了兩種方法,但這里我們不會(huì)搬運(yùn)原文的詳細(xì)內(nèi)容,相反地,下文將簡(jiǎn)要介紹NAC的工作原理,以及它如何處理加減乘除等操作,相應(yīng)代碼也會(huì)在文章中列出,讀者可以從中獲得更直觀的了解。
第一個(gè)神經(jīng)網(wǎng)絡(luò)(NAC)
論文介紹的第一個(gè)神經(jīng)網(wǎng)絡(luò)是神經(jīng)累積器(簡(jiǎn)稱NAC),它能對(duì)輸入執(zhí)行線性變換,而用于變換的矩陣是tanh(What)和sigmoid(Mhat)的元素乘積。簡(jiǎn)而言之,input(x)后,模型輸入會(huì)乘以變換矩陣W,并產(chǎn)生輸出a。
NAC的Python實(shí)現(xiàn):
# 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)
第二個(gè)神經(jīng)網(wǎng)絡(luò)(NALU)
神經(jīng)算術(shù)邏輯單元(NALU)由兩個(gè)NAC構(gòu)成,其中,第一個(gè)NAC g是sigmoid(Gx),第二個(gè)NAC在一個(gè)等于exp(W(log(|x| + epsilon)))的對(duì)數(shù)空間m中運(yùn)行。
NALU的Python實(shí)現(xiàn):
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
現(xiàn)在我們來(lái)進(jìn)行測(cè)試。首先,把NAC轉(zhuǎn)成函數(shù):
# 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
其次,創(chuàng)建一些數(shù)據(jù),把它們分成訓(xùn)練集和測(cè)試集。NumPy有一個(gè)較numpy.arrange的API,很適合用來(lái)創(chuàng)建數(shù)據(jù)集:
# 生成一系列輸入數(shù)字X1和X2用于訓(xùn)練
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)
# 生成一系列輸入數(shù)字X1和X2進(jìn)行測(cè)試
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)
接著,用這些準(zhǔn)備好的東西訓(xùn)練模型。我們先定義占位符X和Y以在運(yùn)行時(shí)提供數(shù)據(jù),用tf.reduce_sum()計(jì)算損失,模型包含兩個(gè)超參數(shù):學(xué)習(xí)率alpha和訓(xùn)練幾個(gè)epochs。在訓(xùn)練開始前,我們還要定義一個(gè)優(yōu)化器,方便用tf.train.AdamOptimizer()降低損失。
# 定義占位符以在運(yùn)行時(shí)提供輸入
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,])
#這里網(wǎng)絡(luò)只包含一個(gè)NAC(用于測(cè)試)
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)
# 訓(xùn)練參數(shù)
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())
# 訓(xùn)練前損失
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()
# 訓(xùn)練后損失
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}))
訓(xùn)練完成后,我們可以得到這樣一幅圖損失曲線圖:
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可以處理諸如加減法的操作,但它還做不到處理乘法和除法。為了解決這個(gè)問題,我們就要用到NALU。
通過乘法理解NALU
在上文基礎(chǔ)上,首先我們?cè)偬砑右粋€(gè)NAC,組成NALU:
如果說NAC只是對(duì)輸入做線性變化,那么NALU就是把兩個(gè)具有權(quán)重的NAC組合在一起,用來(lái)執(zhí)行加減(較小的紫色單元)和乘除(較大的紫色單元),計(jì)算由門(橙色單元)控制。
# 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
這里我們?cè)賱?chuàng)建一些數(shù)據(jù),但和上次相比,這次要做一些改動(dòng):在第8行和第20行,我們把運(yùn)算符從加改成了乘。
# 通過學(xué)習(xí)乘法來(lái)測(cè)試網(wǎng)絡(luò)
# 生成一系列輸入數(shù)字X1和X2用于訓(xùn)練
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)
# 生成一系列輸入數(shù)字X1和X2進(jìn)行測(cè)試
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ùn)練模型,需要注意的是,這里我們定義的還是NAC,而不是NALU:
# 定義占位符以在運(yùn)行時(shí)提供值
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,])
# 定義網(wǎng)絡(luò)
# 這里網(wǎng)絡(luò)只包含一個(gè)NAC(用于測(cè)試)
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)
# 訓(xùn)練參數(shù)
alpha = 0.05 # 學(xué)習(xí)率
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())
# 訓(xùn)練前損失
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()
# 訓(xùn)練后損失
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中實(shí)現(xiàn)NALU的完整代碼,可以去這個(gè)github:github.com/ahylton19/simpleNALU-tf
小結(jié)
以上只是NALU在加減乘除任務(wù)上具體表現(xiàn),在論文中,研究人員還測(cè)試了平方運(yùn)算和開根,NALU的表現(xiàn)都優(yōu)于傳統(tǒng)框架。簡(jiǎn)而言之,DeepMind的這個(gè)簡(jiǎn)單而實(shí)用的技術(shù)讓神經(jīng)網(wǎng)絡(luò)掌握了數(shù)值推算,它類似傳統(tǒng)處理器中的算術(shù)邏輯單元,能讓網(wǎng)絡(luò)真正“學(xué)會(huì)”加減乘除和基于加減乘除的近似估計(jì),更好地把經(jīng)驗(yàn)外推到其他數(shù)值任務(wù)上,而不再受訓(xùn)練數(shù)據(jù)限制。
通過這篇文章,我們希望現(xiàn)在你已經(jīng)了解了這篇轟動(dòng)學(xué)界的論文到底說了什么,以及它對(duì)深度學(xué)習(xí)的貢獻(xiàn)和影響。
-
神經(jīng)網(wǎng)絡(luò)
+關(guān)注
關(guān)注
42文章
4773瀏覽量
100861 -
數(shù)據(jù)
+關(guān)注
關(guān)注
8文章
7073瀏覽量
89137 -
NAC
+關(guān)注
關(guān)注
0文章
5瀏覽量
7714
原文標(biāo)題:TensorFlow推薦:神經(jīng)算術(shù)邏輯單元的直觀理解
文章出處:【微信號(hào):jqr_AI,微信公眾號(hào):論智】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論