哈嘍,大家好。
今天跟大家分享一個非常屌的開源項目,用Numpy發展了一個深度學習框架,文法基本上與 Pytorch 一致。
今天以一個簡單的捲積神經網路為例,分析神經網路訓練過程中,涉及的前向傳播、反向傳播、參數優化等核心步驟的源碼。
使用的資料集和程式碼已經打包好,文末有取得方式。
1. 準備工作
先準備好資料和程式碼。
1.1 建立網路
首先,下載框架原始碼,網址:https://github.com/duma-repo/PyDyNet
git clone https://github.com/duma-repo/PyDyNet.git
建置LeNet卷積神經網絡,訓練三分類模型。
在PyDyNet目錄直接建立程式碼檔案即可。
from pydynet import nn class LeNet(nn.Module): def __init__(self): super().__init__() self.conv1 = nn.Conv2d(1, 6, kernel_size=5, padding=2) self.conv2 = nn.Conv2d(6, 16, kernel_size=5) self.avg_pool = nn.AvgPool2d(kernel_size=2, stride=2, padding=0) self.sigmoid = nn.Sigmoid() self.fc1 = nn.Linear(16 * 5 * 5, 120) self.fc2 = nn.Linear(120, 84) self.fc3 = nn.Linear(84, 3) def forward(self, x): x = self.conv1(x) x = self.sigmoid(x) x = self.avg_pool(x) x = self.conv2(x) x = self.sigmoid(x) x = self.avg_pool(x) x = x.reshape(x.shape[0], -1) x = self.fc1(x) x = self.sigmoid(x) x = self.fc2(x) x = self.sigmoid(x) x = self.fc3(x) return x
可以看到,網路的定義與Pytorch語法完全一樣。
我提供的原始碼裡,提供了 summary 函數可以列印網路結構。
1.2 準備資料
訓練資料使用Fanshion-MNIST資料集,它包含10個類別的圖片,每個類別 6k 張。
為了加快訓練,我只抽了前3個類別,共1.8w張訓練圖片,做一個三分類模型。
1.3 模型訓練
import pydynet from pydynet import nn from pydynet import optim lr, num_epochs = 0.9, 10 optimizer = optim.SGD(net.parameters(), lr=lr) loss = nn.CrossEntropyLoss() for epoch in range(num_epochs): net.train() for i, (X, y) in enumerate(train_iter): optimizer.zero_grad() y_hat = net(X) l = loss(y_hat, y) l.backward() optimizer.step() with pydynet.no_grad(): metric.add(l.numpy() * X.shape[0], accuracy(y_hat, y), X.shape[0])
訓練程式碼也跟Pytorch一樣。
下面重點要做的就是深入模型訓練的源碼,來學習模型訓練的原理。
2. train、no_grad和eval
模型開始訓練前,會呼叫net.train。
def train(self, mode: bool = True): set_grad_enabled(mode) self.set_module_state(mode)
可以看到,它會將grad(梯度)設為True,之後創建的Tensor是可以帶梯度的。 Tensor帶上梯度後,便會將其放入計算圖中,等待求導計算梯度。
而下面的with no_grad(): 程式碼
class no_grad: def __enter__(self) -> None: self.prev = is_grad_enable() set_grad_enabled(False)
會將grad(梯度)設為False,這樣之後建立的Tensor不會放到計算圖中,自然也不需要計算梯度,可以加快推理。
我們常在Pytorch中看到net.eval()的用法,我們也順便看一下它的原始碼。
def eval(self): return self.train(False)
可以看到,它直接呼叫train(False)來關閉梯度,效果與no_grad()類似。
所以,一般在訓練前呼叫train開啟梯度。訓練後,呼叫eval關閉梯度,方便快速推理。
3. 前向傳播
前向傳播除了計算類別機率外,最最重要的一件事是按照前傳順序,將網路中的 tensor 組織成計算圖,目的是為了在反向傳播時計算每個tensor的梯度。
tensor在神經網路中,不只用來儲存數據,還用運算梯度、儲存梯度。
以第一層卷積運算為例,來查看如何產生計算圖。
def conv2d(x: tensor.Tensor, kernel: tensor.Tensor, padding: int = 0, stride: int = 1): '''二维卷积函数 ''' N, _, _, _ = x.shape out_channels, _, kernel_size, _ = kernel.shape pad_x = __pad2d(x, padding) col = __im2col2d(pad_x, kernel_size, stride) out_h, out_w = col.shape[-2:] col = col.transpose(0, 4, 5, 1, 2, 3).reshape(N * out_h * out_w, -1) col_filter = kernel.reshape(out_channels, -1).T out = col @ col_filter return out.reshape(N, out_h, out_w, -1).transpose(0, 3, 1, 2)
x是輸入的圖片,不需要記錄梯度。 kernel是卷積核的權重,需要計算梯度。
所以,pad_x = __pad2d(x, padding) 產生的新的tensor也是不帶梯度的,因此也不需要加入計算圖中。
而kernel.reshape(out_channels, -1)產生的tensor則是需要計算梯度,也需要加入計算圖中。
下面看看加入的過程:
def reshape(self, *new_shape): return reshape(self, new_shape) class reshape(UnaryOperator): ''' 张量形状变换算子,在Tensor中进行重载 Parameters ---------- new_shape : tuple 变换后的形状,用法同NumPy ''' def __init__(self, x: Tensor, new_shape: tuple) -> None: self.new_shape = new_shape super().__init__(x) def forward(self, x: Tensor) return x.data.reshape(self.new_shape) def grad_fn(self, x: Tensor, grad: np.ndarray) return grad.reshape(x.shape)
reshape函數會傳回一個reshape類對象,reshape類繼承了UnaryOperator類,並在__init__函數中,呼叫了父類別初始化函數。
class UnaryOperator(Tensor): def __init__(self, x: Tensor) -> None: if not isinstance(x, Tensor): x = Tensor(x) self.device = x.device super().__init__( data=self.forward(x), device=x.device, # 这里 requires_grad 为 True requires_grad=is_grad_enable() and x.requires_grad, )
UnaryOperator類別繼承了Tensor類,所以reshape物件也是一個tensor。
在UnaryOperator的__init__函數中,呼叫Tensor的初始化函數,並且傳入的requires_grad參數是True,代表需要計算梯度。
requires_grad的計算程式碼為is_grad_enable() and x.requires_grad,is_grad_enable()已經被train設定為True,而x是卷積核,它的requires_grad也是True。
class Tensor: def __init__( self, data: Any, dtype=None, device: Union[Device, int, str, None] = None, requires_grad: bool = False, ) -> None: if self.requires_grad: # 不需要求梯度的节点不出现在动态计算图中 Graph.add_node(self)
最終在Tensor類別的初始化方法中,呼叫Graph.add_node(self)將目前tensor加入到計算圖中。
同理,下面使用requires_grad=True的tensor常見出來的新tensor都會放到計算圖中。
經過一次卷積運算,計算圖中會增加 6 個節點。
4. 反向傳播
一次前向傳播完成後,從計算圖中最後一個節點開始,從後往前反向傳播。
l = loss(y_hat, y) l.backward()
經過前向網路一層層傳播,最後傳到了損失張量l。
以l為起點,從前向後傳播,就可計算計算圖中每個節點的梯度。
backward的核心程式碼如下:
def backward(self, retain_graph: bool = False): for node in Graph.node_list[y_id::-1]: grad = node.grad for last in [l for l in node.last if l.requires_grad]: add_grad = node.grad_fn(last, grad) last.grad += add_grad
Graph.node_list[y_id::-1]將計算圖倒序排。
node是前向传播时放入计算图中的每个tensor。
node.last 是生成当前tensor的直接父节点。
调用node.grad_fn计算梯度,并反向传给它的父节点。
grad_fn其实就是Tensor的求导公式,如:
class pow(BinaryOperator): ''' 幂运算算子,在Tensor类中进行重载 See also -------- add : 加法算子 ''' def grad_fn(self, node: Tensor, grad: np.ndarray) if node is self.last[0]: return (self.data * self.last[1].data / node.data) * grad
return后的代码其实就是幂函数求导公式。
假设y=x^2,x的导数为2x。
5. 更新参数
反向传播计算梯度后,便可以调用优化器,更新模型参数。
l.backward() optimizer.step()
本次训练我们用梯度下降SGD算法优化参数,更新过程如下:
def step(self): for i in range(len(self.params)): grad = self.params[i].grad + self.weight_decay * self.params[i].data self.v[i] *= self.momentum self.v[i] += self.lr * grad self.params[i].data -= self.v[i] if self.nesterov: self.params[i].data -= self.lr * grad
self.params是整个网络的权重,初始化SGD时传进去的。
step函数最核心的两行代码,self.v[i] += self.lr * grad 和 self.params[i].data -= self.v[i],用当前参数 - 学习速率 * 梯度更新当前参数。
这是机器学习的基础内容了,我们应该很熟悉了。
一次模型训练的完整过程大致就串完了,大家可以设置打印语句,或者通过DEBUG的方式跟踪每一行代码的执行过程,这样可以更了解模型的训练过程。
以上是逆天了!以Numpy發展深度學習框架,透視神經網路訓練過程的詳細內容。更多資訊請關注PHP中文網其他相關文章!

大型語言模型(LLMS)的流行激增,工具稱呼功能極大地擴展了其功能,而不是簡單的文本生成。 現在,LLM可以處理複雜的自動化任務,例如Dynamic UI創建和自主a

視頻遊戲可以緩解焦慮,建立焦點或支持多動症的孩子嗎? 隨著醫療保健在全球範圍內挑戰,尤其是在青年中的挑戰,創新者正在轉向一種不太可能的工具:視頻遊戲。現在是世界上最大的娛樂印度河之一

“歷史表明,儘管技術進步推動了經濟增長,但它並不能自行確保公平的收入分配或促進包容性人類發展,”烏托德秘書長Rebeca Grynspan在序言中寫道。

易於使用,使用生成的AI作為您的談判導師和陪練夥伴。 讓我們來談談。 對創新AI突破的這種分析是我正在進行的《福布斯》列的最新覆蓋範圍的一部分,包括識別和解釋

在溫哥華舉行的TED2025會議昨天在4月11日舉行了第36版。它的特色是來自60多個國家 /地區的80個發言人,包括Sam Altman,Eric Schmidt和Palmer Luckey。泰德(Ted)的主題“人類重新構想”是量身定制的

約瑟夫·斯蒂格利茨(Joseph Stiglitz)是2001年著名的經濟學家,是諾貝爾經濟獎的獲得者。斯蒂格利茨認為,AI可能會使現有的不平等和合併權力惡化,並在一些主導公司手中加劇,最終破壞了經濟上的經濟。

圖數據庫:通過關係徹底改變數據管理 隨著數據的擴展及其特徵在各個字段中的發展,圖形數據庫正在作為管理互連數據的變革解決方案的出現。與傳統不同

大型語言模型(LLM)路由:通過智能任務分配優化性能 LLM的快速發展的景觀呈現出各種各樣的模型,每個模型都具有獨特的優勢和劣勢。 有些在創意內容gen上表現出色


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

Safe Exam Browser
Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

MantisBT
Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

SecLists
SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

ZendStudio 13.5.1 Mac
強大的PHP整合開發環境