23. 深度學習 - 多維向量自動求導

茶桁的AI秘籍 核心能力 23

Hi, 你好。我是茶桁。

前面幾節課中,我們從最初的理解神經網絡,到講解函數,多層神經網絡,拓樸排序以及自動求導。 可以說,最難的部分已經過去了,這節課到了我們來收尾的階段,沒錯,生長了這么久,終于到迎接成果的時候了。

好,讓我們開始。

我們還是用上一節課的代碼:21.ipynb

我們上一節課中,實現了自動計算的部分。

for node in sorted_nodes[::-1]:print('\n{}'.format(node.name))node.backward()

結果我就不打印了,節省篇幅。

那我們到這一步之后,咱們就已經獲得了偏導,現在要考慮的問題就是去更新它,去優化它的值。

learning_rate = 1e-5for node in sorted_nodes:node.value = node.value + -1 * node.gradients[node] * learning_rate

node的值去更新,就應該等于它本身的值加上一個-1乘以它的偏導在乘以一個learning_rate, 我們對這個是不是已經很熟悉了?我們從第8節線性回歸的時候就一直在接觸這個公式。

只不過在這個地方,x, y的值也要更新嗎? 它們的值是不應該去更新的,那要更新的應該是k, b的值。

那么在這個地方該怎么辦呢?其實很簡單,我們添加一個判斷就可以了:

for node in sorted_nodes:if node.is_trainable:node.value = node.value + -1 * node.gradients[node] * learning_rate

然后我們給之前定義的類上加一個變量用于判斷。

class Node:def __init__(..., is_trainable=False):...self.is_trainable = is_trainable

在這里我們默認是不可以訓練的,只有少數的一些是需要訓練的。

然后我們在初始化的部分把這個定義的值加上:

node_k = Placeholder(name='k', is_trainable=True)
node_b = Placeholder(name='b', is_trainable=True)

對了,我們還需要將Placeholder做些改變:

class Placeholder(Node):def __init__(..., is_trainable=False):Node.__init__(.., is_trainable=is_trainable)......

這就意味著,運行for循環的時候只有k和b的值會更新,我們再加幾句話:

for node in sorted_nodes:if node.is_trainable:...cmp = 'large' if node.gradients[node] > 0 else 'small'print('{}的值{},需要更新。'.format(node.name, cmp))---
k的值small,需要更新。
b的值small,需要更新。

我們現在將forward, backward和optimize的三個循環封裝乘三個方法:

def forward(graph_sorted_nodes):# Forwardfor node in sorted_nodes:node.forward()def backward(graph_sorted_nodes):# Backwardfor node in sorted_nodes[::-1]:print('\n{}'.format(node.name))node.backward()def optimize(graph_sorted_nodes, learning_rate=1e-3):# optimizefor node in sorted_nodes:if node.is_trainable:node.value = node.value + -1 * node.gradients[node] * learning_ratecmp = 'large' if node.gradients[node] > 0 else 'small'print('{}的值{},需要更新。'.format(node.name, cmp))

然后我們再來定義一個epoch方法,將forward和backward放進去一起執行:

def run_one_epoch(graph_sorted_nodes):forward(graph_sorted_nodes)backward(graph_sorted_nodes)

這樣,我們完成一次完整的求值-求導-更新,就可以寫成這樣:

run_one_epoch(sorted_nodes)
optimize(sorted_nodes)

為了更好的觀察,我們將所有的print都刪掉,然后在backward方法中寫一個觀察loss的打印函數:

def backward(graph_sorted_nodes):# Backwardfor node in sorted_nodes[::-1]:if isinstance(node, Loss):print('loss value: {}'.format(node.value))node.backward()

然后我們來對剛才完整的過程做個循環:

# 完整的一次求值-求導-更新:
for _ in range(10):run_one_epoch(sorted_nodes)optimize(sorted_nodes, learning_rate=1e-1)---
loss value: 0.12023025149136042
loss value: 0.11090709486917472
loss value: 0.10118818479676453
loss value: 0.09120180962480523
loss value: 0.08111466190584131
loss value: 0.0711246044819575
loss value: 0.061446239826641165
loss value: 0.05229053883349982
loss value: 0.043842158831920566
loss value: 0.036239620745126

可以看到loss在一點點的下降。當然,這樣循環10次我們還能觀察出來,但是我們如果要成百上千次的去計算它,這樣可就不行了, 那我們需要將history存下來,然后用圖來顯示出來:

loss_history = []
for _ in range(100):..._loss_node = sorted_nodes[-1]assert isinstance(_loss_node, Loss)loss_history.append(_loss_node.value)optimize(sorted_nodes, learning_rate=1e-1)plt.plot(loss_history)

Alt text

我們現在可以驗證一下,我們擬合的yhat和真實的y之間差距有多大,首先我們當然是要獲取到每個值的下標,然后用sigmoid函數來算一下:

sorted_nodes---
[k, y, x, b, Linear, Sigmoid, Loss]

通過下標來進行計算, k是0, x是2, b是3, y是1:

def sigmoid(x):return 1/(1+np.exp(-x))# k*x+b
sigmoid_x = sorted_nodes[0].value * sorted_nodes[2].value + sorted_nodes[3].value
print(sigmoid(sigmoid_x))# y
print(sorted_nodes[1].value)---
0.891165479601981
0.8988713384533658

可以看到,非常的接近。那說明我們擬合的情況還是不錯的。

好,這里總結一下,就是我們有了拓樸排序,就能向前去計算它的值,通過向前計算的值就可以向后計算它的值。那現在其實我們已經完成了一個mini的深度學習框架的核心內容,咱們能夠定義節點,能夠前向傳播運算,能夠反向傳播運算,能更新梯度了。

那接下來是不是就結束了呢?很遺憾,并沒有,接著咱們還要考慮如何處理多維數據。咱們現在看到的數據都是x、k、b的輸入,也就是都是一維的。

然而咱們真實世界中大多數場景下其實都是多維度的,其實都是多維數組。那么多維數組的還需要更新些什么,和現在有什么區別呢?

我們來接著往后看,因為基本上寫法和現在這些幾乎完全一樣,那我也就不這么細致的講了。

為了和之前代碼做一個區分,所以我將多維向量計算的代碼從新開了個文件,放在了23.ipynb里,小伙伴可以去下載到本地研習。

那么多維和現在最大的區別在哪里呢?就在于計算的時候,我們就要用到矩陣運算了。只是值變成了矩陣,運算變成的了矩陣運算。好,我們從Node開始來改動它,沒什么變化的地方我就直接用...來省略了:

class Node:def __init__(self, input=[]):...def forward(self):raise NotImplementeddef backward(self):raise NotImplementedclass Placeholder(Node):def __init__(self):Node.__init__(self)def forward(self, value=None):...def backward(self):self.gradients = {self:0}for n in self.outputs:grad_cost = n.gradients[self]self.gradients[self] = grad_cost * 1class Linear(Node):def __init__(self, x, k, b):...def forward(self):...def backward(self):self.gradients = {n: np.zeros_like(n.value) for n in self.inputs}for n in self.outputs:grad_cost = n.gradients[self]self.gradients[self.inputs[0]] = np.dot(grad_cost, self.inputs[1].value.T)self.gradients[self.inputs[1]] = np.dot(self.inputs[0].value.T, grad_cost)self.gradients[self.inputs[2]] = np.sum(grad_cost, axis=0, keepdims=False)class Sigmoid(Node):def __init__(self, node):Node.__init__(self, [node])def _sigmoid(self, x):...def forward(self):...def backward(self):self.partial = self._sigmoid(self.x) * (1 - self._sigmoid(self.x))self.gradients = {n: np.zeros_like(n.value) for n in self.inputs}for n in self.outputs:grad_cost = n.gradients[self]  self.gradients[self.inputs[0]] = grad_cost * self.partialclass MSE(Node): # 也就是之前的Loss類def __init__(self, y, a):Node.__init__(self, [y, a])def forward(self):y = self.inputs[0].value.reshape(-1, 1)a = self.inputs[1].value.reshape(-1, 1)assert(y.shape == a.shape)self.m = self.inputs[0].value.shape[0]self.diff = y - aself.value = np.mean(self.diff**2)def backward(self):self.gradients[self.inputs[0]] = (2 / self.m) * self.diffself.gradients[self.inputs[1]] = (-2 / self.m) * self.diff

類完成之后,我們還有一些其他的方法:

def forward_and_backward(graph): # run_one_epochfor n in graph:n.forward()for n in  graph[::-1]:n.backward()
def toplogic(graph):...
def convert_feed_dict_to_graph(feed_dict):...
# 將sorted_nodes賦值從新定義了一個方法
def topological_sort_feed_dict(feed_dict):graph = convert_feed_dict_to_graph(feed_dict)return toplogic(graph)def optimize(trainables, learning_rate=1e-2):for node in trainables:node.value += -1 * learning_rate * node.gradients[node]

這樣就完成了。可以發現基本上代碼沒有什么變動,變化比較大的都是各個類中的backward方法,因為要將其變成使用矩陣運算。

我們來嘗試著用一下這個多維算法,我們還是用波士頓房價的那個數據來做一下嘗試:

X_ = data['data']
y_ = data['target']# Normalize data
X_ = (X_ - np.mean(X_, axis=0)) / np.std(X_, axis=0)n_features = X_.shape[1]
n_hidden = 10
W1_ = np.random.randn(n_features, n_hidden)
b1_ = np.zeros(n_hidden)
W2_ = np.random.randn(n_hidden, 1)
b2_ = np.zeros(1)# Neural network
X, y = Placeholder(), Placeholder()
W1, b1 = Placeholder(), Placeholder()
W2, b2 = Placeholder(), Placeholder()l1 = Linear(X, W1, b1)
s1 = Sigmoid(l1)
l2 = Linear(s1, W2, b2)
cost = MSE(y, l2)feed_dict = {X: X_,y: y_,W1: W1_,b1: b1_,W2: W2_,b2: b2_
}epochs = 5000
# Total number of examples
m = X_.shape[0]
batch_size = 16
steps_per_epoch = m // batch_sizegraph = topological_sort_feed_dict(feed_dict)
trainables = [W1, b1, W2, b2]print("Total number of examples = {}".format(m))

我們在中間定義了l1, s1, l2, cost, 分別來實例化四個類。然后我們就需要根據數據來進行迭代計算了,定義一個losses來保存歷史數據:

losses = []epochs = 100for i in range(epochs):loss = 0for j in range(steps_per_epoch):# Step 1X_batch, y_batch = resample(X_, y_, n_samples=batch_size)X.value = X_batchy.value = y_batch# Step 2forward_and_backward(graph) # set output node not important.# Step 3rate = 1e-2optimize(trainables, rate)loss += graph[-1].valueif i % 100 == 0: print("Epoch: {}, Loss: {:.3f}".format(i+1, loss/steps_per_epoch))losses.append(loss/steps_per_epoch)---
Epoch: 1, Loss: 194.170
...
Epoch: 4901, Loss: 3.137

可以看到它loss下降的非常快,還記得咱們剛開始的時候在訓練波士頓房價數據的時候,那個loss下降到多少? 最低是不是就下降到在第一節課的時候我們的lose最多下降到了多少47.34對吧?那現在呢?直接下降到了3,這是為什么? 因為我們的維度多了,維度多了它就準確了。這說明什么? 說明大家去談戀愛的時候,不要盯著對象的一個方面,多方面考察,才能知道這個人是否合適。

好,現在看起來效果是很好,但是我們想知道到底擬合出來的什么函數,那怎么辦?咱們把這個維度降低成三維空間就可以看了。

現在咱們這個波士頓的所有數據實際上是一個15維的數據,15維的數據你根本看不了,咱們現在只要把x這個里邊取一點值,在這個里邊稍微把值給它變一下。

X_ = dataframe[['RM', 'LSTAT']]
y_ = data['target']

在咱們之前的課程中對其進行計算的時候就分析過,RM和LSTAT是影響最大的兩個特征,我們還是來用這個。然后我們將剛才的代碼從新運行一遍:

losses = []for i in tqdm_notebook(range(epochs)):...---
Epoch: 1, Loss: 150.122
...
Epoch: 4901, Loss: 16.181

這次下降的就沒上次好了。

現在我們可視化一下這個三維空間來看看:

from mpl_toolkits.mplot3d import Axes3Dpredicate_results = []
for rm, ls in X_.values:X.value = np.array([[rm, ls]])forward_and_backward(graph)predicate_results.append(graph[-2].value[0][0])%matplotlib widgetfig = plt.figure(figsize=(10, 10))
ax = fig.add_subplot(111, projection='3d')X_ = dataframe[['RM', 'LSTAT']].values[:, 0]
Y_ = dataframe[['RM', 'LSTAT']].values[:, 1]Z = predicate_resultsrm_and_lstp_price = ax.plot_trisurf(X_, Y_, Z, color='green')ax.set_xlabel('RM')
ax.set_ylabel('% of lower state')
ax.set_zlabel('Predicated-Price')

然后我們就能看到一個數據的三維圖形,因為我們開啟了widget, 所以可以進行拖動。

Alt text

Alt text

從圖形上看,確實符合房間越多,低收入人群越少,房價越高的特性。

那現在計算機確實幫我們自動的去找到了一個函數,這個函數到底怎么設置咱們都不用關心,它自動就給你求解出來,這個就是深度學習的意義。咱們經過這一系列寫出來的東西其實就已經能夠做到。

我覺得這個真的有一種數學之美,它從最簡單的東西出發,最后做成了這樣一個復雜的東西。確實很深其,并且還都在我們的掌握之中。

好,大家下來以后記得要多多自己敲代碼,多分析其中的一些過程和原理。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/162702.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/162702.shtml
英文地址,請注明出處:http://en.pswp.cn/news/162702.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Android定位make有哪些target $ mgrep “build“ |grep target |grep image

環境 $ cat /proc/cpuinfo processor : 0 vendor_id : GenuineIntel cpu family : 6 model : 85 model name : Intel(R) Xeon(R) Gold 5122 CPU 3.60GHz stepping : 4 microcode : 0x2006e05 cpu MHz : 1200.086 cache…

大模型訓練效率提升至2.6倍,騰訊Angel機器學習框架升級

在算力緊缺的背景下,如何提升大模型訓練和推理的效率,并降低成本,成為業界關注的焦點。 11月23日,騰訊披露,騰訊混元大模型背后的自研機器學習框架Angel再次升級,大模型訓練效率提升至主流開源框架的2.6倍…

SQL Server刪除重復數據只保留一條

介紹 最近在導入數據庫數據, 有時候給的數據源文件,存在重復數據, 需要清除但是還需要保留一條記錄的需求. 本文將介紹如何使用SQL Server來實現這個需求。 流程 下面是實現刪除重復數據的流程,我們可以用表格展示每個步驟: 步驟 描述 步驟一 先…

算法通關村第十二關-白銀挑戰字符串經典題目

大家好我是蘇麟 , 今天帶來字符串相關的題目 . 大綱 反轉問題字符串反轉K個一組反轉僅僅反轉字母反轉字符串中的單詞 反轉問題 字符串反轉 描述 : 編寫一個函數,其作用是將輸入的字符串反轉過來。輸入字符串以字符數組 s的形式給出。 題目 : LeetCode 344. 反轉…

webshell之擴展免殺

由于很多企業為了防止源碼泄露,都會使用加密擴展將代碼進行加密,那么我們就可以就將計就計,將webshell也利用擴展加密,將特征消除,從而達到免殺的效果 1.php-beast 擴展地址 下載dll,并添加至ext中 在php…

優化數據分析——理解與運用各類指標

寫在開頭 數據分析在當今信息時代扮演著至關重要的角色,而指標則是我們理解數據、揭示模式、支持決策的關鍵工具。本文將深入討論各類指標的應用場景和解讀方法,以幫助更全面、深入地理解數據。 1. 中心趨勢指標 1.1 均值:更深層次的理解 …

MySQL中自增id用完怎么辦?

MySQL中自增id用完怎么辦? MySQL里有很多自增的id,每個自增id都是定義了初始值,然后不停地往上加步長。雖然自然數是沒有上限的,但是在計算機里,只要定義了表示這個數的字節長度,那它就有上限。比如&#…

【2023持續更新】網絡安全工程師常用工具集合

文章目錄 SQL注入檢測 SQL注入檢測 https://github.com/r0oth3x49/ghauri

python數據結構與算法-15_堆與堆排序

堆(heap) 前面我們講了兩種使用分治和遞歸解決排序問題的歸并排序和快速排序,中間又穿插了一把樹和二叉樹, 本章我們開始介紹另一種有用的數據結構堆(heap), 以及借助堆來實現的堆排序,相比前兩種排序算法要稍難實現一些。 最后我…

Linux開發工具(含gdb調試教程)

文章目錄 Linux開發工具(含gdb調試教程)1、Linux 軟件包管理器 yum2、Linux開發工具2.1、Linux編輯器 -- vim的使用2.1.1、vim的基本概念2.1.2、vim的基本操作2.1.3、vim正常模式命令集2.1.4、vim末行模式命令集 2.2、vim簡單配置 3、Linux編譯器 -- gcc…

HIVE SQL取整函數匯總

目錄 int()round(double a)round(double a,int d)floor()ceil() int() 向零取整,即向接近零的方向取整。 int(5.6)輸出:5 int(-5.6)輸出:-5 round(double a) 四舍五入取整 select round(5.6)輸出:6 select round(-5.6)輸出&…

關于前端處理后端輪詢的操作 (總結)

使用場景:前端首次發起請求獲取數據,若失敗則每隔1s發起一次知道成功獲取數據為止解決方案: 使用輪詢操作,涉及定時器的使用和關閉 (使用vue2代碼為例) data() {return {pollingResult_en: null, // 處理輪詢結果bizI…

redis之cluster集群

1、redis-cluster集群:redis3.0引入的分布式存儲方案 2、集群:由多個node節點組成,redis數據分布在這些節點之中 (1)在集群之中也分主節點和從節點 (2)自帶哨兵模式 3、redis-cluster集群的…

騰訊云 小程序 SDK對象存儲 COS使用記錄,原生小程序寫法。

最近做了一個項目,需求是上傳文檔,文檔類型多種,圖片,視頻,文件,doc,xls,zip,txt 等等,而且文檔類型可能是大文件,可能得上百兆,甚至超過1G。 騰訊云文檔地址:https://c…

Java接口自動化測試系列[V1.0.0][概述]

基礎知識 在TCP/IP中,HTTP屬于傳輸層協議,該協議采用的是Request-Response的模式,且該協議是無狀態的,也就是后續如果要用到前面的信息必須重新請求重新獲取;HTTP通過SSL/TSL加密成為HTTPS,與HTTP相比HTTP…

PC端頁面進去先出現加載效果

自定義指令v-loading&#xff0c;只需要綁定Boolean即可 v-loading“loading” <el-table :data"list" border style"width: 100%" v-loading"loading"><el-table-column align"center" label"序號" width"5…

開發板啟動進入系統以后再掛載 NFS 文件系統, 這里的NFS文件系統是根據正點原子教程制作的ubuntu_rootfs

如果是想開發板啟動進入系統以后再掛載 NFS 文件系統&#xff0c;開發板啟動進入文件系統&#xff0c;開發板和 ubuntu 能互相 ping 通&#xff0c;在開發板文件系統下新建一個目錄 you&#xff0c;然后執行如下指令進行掛載&#xff1a; mkdir mi mount -t nfs -o nolock,nfsv…

Hive日志默認存儲在什么位置?

在hive-log4j.properties配置文件中&#xff0c;有這么一段配置信息 hive.log.thresholdALL hive.root.loggerWARN,DRFA hive.log.dir${java.io.tmpdir}/${user.name} hive.log.filehive.log hive.log.dir就是日志存儲在目錄/tmp/${user.name}(當前用戶名)/下 而hive.log就是h…

日本it就職培訓機構,日本IT行業的三種類型

日本的IT產業一直保持增長趨勢&#xff0c;市場規模逐年增加&#xff0c;在日本所有產業中占據很大比例。由于日本老齡化嚴重&#xff0c;日本國內的IT人才無法滿足需求&#xff0c;為緩解這一問題&#xff0c;日本將引進外國優秀IT人才作為一項國策&#xff0c;日本IT行業不僅…

Leetcode1410. HTML 實體解析器

Every day a Leetcode 題目來源&#xff1a;1410. HTML 實體解析器 解法1&#xff1a;模擬 遍歷字符串 text&#xff0c;每次遇到 ’&‘&#xff0c;就判斷以下情況&#xff1a; 雙引號&#xff1a;字符實體為 &quot; &#xff0c;對應的字符是 " 。單引號&a…