RNN二進位制加法例項
本文是根據前兩篇詳細展示RNN的網路結構以及詳細闡述基於時間的反向傳播演算法(Back-Propagation Through Time,BPTT)來找的一個RNN例項,本例子可以幫助對RNN的前向傳播以及後向傳播,以及RNN結構的理解。整個過程符合下圖RNN結構描述:
# -*- coding: utf-8 -*-
"""
Created on Mon Sep 24 17:02:41 2018
@author: Abner_hg
"""
import numpy as np
import copy
def sigmoid(x): #sigmoid
return 1/(1+np.exp(-x))
# convert output of sigmoid function to its derivative #計算sigmoid函式的倒數
def sigmoid_output_to_derivative(output): #Sigmoid函式求導
return output*(1-output)
binary_dim = 8 #二進位制的長度
int2binary = {}#儲存數字及其對應的二進位制表示
largest_num = pow(2, binary_dim) # 生成binary_dim所能表示的最大的數字
num_arr = np.array([range(largest_num)], dtype = np.uint8).T #生成0到largest之間的所有數字
binary = np.unpackbits(num_arr, axis = 1) #將對應的數字轉為二進位制
for i in range(largest_num): #儲存數字及其對應的二進位制
int2binary[i] = binary[i]
alpha = 0.1 #學習率
input_dim = 2 #輸入的維度
hidden_dim = 16 #隱藏層神經元的個數
output_dim = 1 #輸出的維度
W = 2 * np.random.random((input_dim, hidden_dim)) - 1 #輸入至神經元的w0,維度為2X16,取值約束在[-1,1]間
U = 2 * np.random.random((hidden_dim, hidden_dim)) - 1 #神經元至輸出層的權重w1,維度為16X1,取值約束在[-1,1]間
V = 2 * np.random.random((hidden_dim, output_dim)) - 1 #神經元前一時刻狀態至當前狀態權重wh,維度為16X16,取值約束在[-1,1]間
W_update = np.zeros_like(W) #構造與W相同維度的矩陣,並初始化為全0,用於存放所有時刻產生W的梯度;
U_update = np.zeros_like(U) #構造與U相同維度的矩陣,並初始化為全0,用於存放所有時刻產生U的梯度;
V_update = np.zeros_like(V) #構造與V相同維度的矩陣,並初始化為全0,用於存放所有時刻產生V的梯度;
for epoch in range(10000): #總共迭代10000次
a_int = np.random.randint(largest_num >> 2) #在小於largest_num / 2之中隨機挑選一個數字
a = int2binary[a_int] #a_int對應的二進位制
b_int = np.random.randint(largest_num - a_int) #在小於largest_num -a_int之中隨機挑選一個數字,之所以這麼選取,是為了防止a_int+b_int超出了largest_num,還要重新轉化為二進位制,沒有查字典快,當然,這裡也可以修改
b = int2binary[b_int] #b_int對應的二進位制
c_int = a_int + b_int
c = int2binary[c_int] #c_int對應的二進位制
predict = np.zeros_like(c) #predict就是預測結果,因此和真正值c的二進位制維度是一樣的
total_error = 0 #總的損失,等價於L
all_ht_values = list() #隱藏層的輸出
all_ht_values.append(np.zeros(hidden_dim)) #8*16,用於存放所有的ht的值
delta_zt_values = list() #存放所有豎直方向的梯度,即每個時刻損失,對輸出層的輸入求導
for pos in range(binary_dim): #正向傳播,從低位開始計算,也就是從右至左
#生成輸入和輸出
x = np.array([[a[binary_dim - pos - 1], b[binary_dim - pos -1]]]) #每次從a和b的低位各取出1位
y = np.array([[c[binary_dim-pos-1]]]).T #對應的真實值
pre_ht = all_ht_values[-1] #前一時刻隱藏層的輸出,即ht-1
ht = sigmoid(np.dot(x, W) + np.dot(pre_ht, U)) #當前時刻隱藏層的輸出,即ht
y_hat = sigmoid(np.dot(ht, V)) #當前時刻輸出層的輸出
predict[binary_dim - pos - 1] = np.round(y_hat[0][0]) #predict預測值
Lt = y - y_hat #每一時刻產生的損失
delta_zt_values.append((Lt)*sigmoid_output_to_derivative(y_hat)) #每一時刻都要計算,豎直方向的損失,對輸出層的輸入的導數
total_error += np.abs(Lt[0]) #加入總的損失
all_ht_values.append(copy.deepcopy(ht)) #存入當前時刻隱藏層的輸出,以便後續需要
#到目前為止binary_dim位的二進位制已經計算完畢,接下來就是反向傳播進行引數更新,BPTT
#我們記pre_delta_st = delta_t = delta Lt / delta st,則:
pre_delta_st = np.zeros(hidden_dim)
for pos in range(binary_dim):
X = np.array([[a[pos], b[pos]]]) #當前時刻,所對應的二進位制輸入
ht = all_ht_values[-pos-1]
pre_ht = all_ht_values[-pos-2]
delta_zt = delta_zt_values[-pos-1] #當前時刻,豎直方向貢獻的損失值,這個正向傳播的時候已經求出
#delta L / delta st = (delta L /delta ht) * (delta ht / delta st)
# = [(delta L /delta zt) * (delta zt /delta ht) + (delta L /delta st+1) * (delta st+1 /delta ht)] * (delta ht / delta st)
# = [(V.T * delta_zt) + (U.T * delta_st+1)] * sigmoid_output_to_derivative(ht)
delta_st = (delta_zt.dot(V.T) + pre_delta_st.dot(U.T)) * sigmoid_output_to_derivative(ht)
V_update += np.atleast_2d(ht).T.dot(delta_zt)#計入損失
W_update += X.T.dot(delta_st)
U_update += np.atleast_2d(pre_ht).T.dot(delta_st)
pre_delta_st = delta_st
W += W_update * alpha
V += V_update * alpha
U += U_update * alpha
W_update *= 0
V_update *= 0
U_update *= 0
# print out progress
if(epoch % 500 == 0): #每500次列印結果
print (" a_binary: " + str(a))
print ("+ b_binary: " + str(b))
print ("--------------------------------")
print (" Predict: " + str(predict))
out = 0
for index,x in enumerate(reversed(predict)):
out += x*pow(2,index)
print (" a + b: "+str(a_int) + " + " + str(b_int) + " = " + str(out))
print ('\n')
print (" True_value: " + str(c))
print (" Error: " + str(total_error))
print ('\n')
print ("##################################")
print ("##################################")
print ('\n')
參考:
Anyone Can Learn To Code an LSTM-RNN in Python (Part 1: RNN)
相關文章
- python進位制轉換(二進位制、十進位制和十六進位制)及注意事項Python
- 加強版二進位制讀寫器
- 二進位制與二進位制運算
- 十進位制轉換為十六進位制和二進位制程式碼例項
- 進位制詳解:二進位制、八進位制和十六進位制
- JavaScript 二進位制、八進位制與十六進位制JavaScript
- (二進位制)
- 二進位制
- 十進位制——二 (八、十六 )進位制
- 二進位制,八進位制,十進位制,十六進位制的相互轉換
- 【進位制轉換】二進位制、十六進位制、十進位制、八進位制對應關係
- N位二進位制數加減法運算圖靈機圖靈
- 二進位制、十進位制與十六進位制相互轉化
- java中二進位制、八進位制、十進位制、十六進位制的轉換Java
- 二進位制,八進位制,十進位制,十六進位制之間的轉換
- Python 進位制互相轉換(二進位制、十進位制和十六進位制)Python
- 計算機基礎進位制轉換(二進位制、八進位制、十進位制、十六進位制)計算機
- 二進位制轉十進位制快速方法
- JAVA 二進位制,八進位制,十六進位制,十進位制間進行相互轉換Java
- 什麼是二進位制?二進位制如何轉換?
- 04 二進位制
- 大話二進位制,八進位制,十進位制,十六進位制之間的轉換
- JavaScript十進位制轉換為二進位制JavaScript
- Oracle二進位制與十進位制轉換Oracle
- 十進位制轉二進位制推導(草稿)
- [計算機基礎] 計算機進位制轉換:二進位制、八進位制、十進位制、十六進位制計算機
- 一看就懂二進位制、八進位制、十六進位制數轉換十進位制
- Oracle中的二進位制、八進位制、十進位制、十六進位制相互轉換函式Oracle函式
- 進位制之間的轉換之“十六進位制 轉 十進位制 轉 二進位制 方案”
- 整數轉化成八進位制、十六進位制、二進位制,以及轉回
- 十進位制與二進位制互相轉換指南
- 二進位制轉十進位制快速轉換方法
- 位,位元組,二進位制,十六進位制間的關係
- Cocoapods 二進位制
- 權勢二進位制
- 二進位制字串相加字串
- 二進位制陣列陣列
- 二進位制或序列