<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          收藏 | 如何自己設(shè)計(jì)實(shí)現(xiàn)一個(gè)深度學(xué)習(xí)框架

          共 20927字,需瀏覽 42分鐘

           ·

          2023-08-17 04:38

          點(diǎn)擊上方小白學(xué)視覺”,選擇加"星標(biāo)"或“置頂

          重磅干貨,第一時(shí)間送達(dá)

          當(dāng)前深度學(xué)習(xí)框架越來越成熟,對于使用者而言封裝程度越來越高,好處就是現(xiàn)在可以非常快速地將這些框架作為工具使用,用非常少的代碼就可以構(gòu)建模型進(jìn)行實(shí)驗(yàn),壞處就是可能背后地實(shí)現(xiàn)都被隱藏起來了。在這篇文章里筆者將設(shè)計(jì)和實(shí)現(xiàn)一個(gè)、輕量級的(約 200 行)、易于擴(kuò)展的深度學(xué)習(xí)框架 tinynn(基于 Python 和 Numpy 實(shí)現(xiàn)),希望對大家了解深度學(xué)習(xí)的基本組件、框架的設(shè)計(jì)和實(shí)現(xiàn)有一定的幫助。

          本文首先會(huì)從深度學(xué)習(xí)的流程開始分析,對神經(jīng)網(wǎng)絡(luò)中的關(guān)鍵組件抽象,確定基本框架;然后再對框架里各個(gè)組件進(jìn)行代碼實(shí)現(xiàn);最后基于這個(gè)框架實(shí)現(xiàn)了一個(gè) MNIST 分類的示例,并與 Tensorflow 做了簡單的對比驗(yàn)證。

          目錄

          1. 組件抽象
          2. 組件實(shí)現(xiàn)
          3. 整體結(jié)構(gòu)
          4. MNIST 例子
          5. 總結(jié)
          6. 附錄
          7. 參考

          組件抽象

          首先考慮神經(jīng)網(wǎng)絡(luò)運(yùn)算的流程,神經(jīng)網(wǎng)絡(luò)運(yùn)算主要包含訓(xùn)練 training 和預(yù)測 predict (或 inference) 兩個(gè)階段,訓(xùn)練的基本流程是:輸入數(shù)據(jù) -> 網(wǎng)絡(luò)層前向傳播 -> 計(jì)算損失 -> 網(wǎng)絡(luò)層反向傳播梯度 -> 更新參數(shù),預(yù)測的基本流程是 輸入數(shù)據(jù) -> 網(wǎng)絡(luò)層前向傳播 -> 輸出結(jié)果。從運(yùn)算的角度看,主要可以分為三種類型的計(jì)算:

          1. 數(shù)據(jù)在網(wǎng)絡(luò)層之間的流動(dòng):前向傳播和反向傳播可以看做是張量 Tensor(多維數(shù)組)在網(wǎng)絡(luò)層之間的流動(dòng)(前向傳播流動(dòng)的是輸入輸出,反向傳播流動(dòng)的是梯度),每個(gè)網(wǎng)絡(luò)層會(huì)進(jìn)行一定的運(yùn)算,然后將結(jié)果輸入給下一層
          2. 計(jì)算損失:銜接前向和反向傳播的中間過程,定義了模型的輸出與真實(shí)值之間的差異,用來后續(xù)提供反向傳播所需的信息
          3. 參數(shù)更新:使用計(jì)算得到的梯度對網(wǎng)絡(luò)參數(shù)進(jìn)行更新的一類計(jì)算

          基于這個(gè)三種類型,我們可以對網(wǎng)絡(luò)的基本組件做一個(gè)抽象

          • tensor 張量,這個(gè)是神經(jīng)網(wǎng)絡(luò)中數(shù)據(jù)的基本單位
          • layer 網(wǎng)絡(luò)層,負(fù)責(zé)接收上一層的輸入,進(jìn)行該層的運(yùn)算,將結(jié)果輸出給下一層,由于 tensor 的流動(dòng)有前向和反向兩個(gè)方向,因此對于每種類型網(wǎng)絡(luò)層我們都需要同時(shí)實(shí)現(xiàn) forward 和 backward 兩種運(yùn)算
          • loss 損失,在給定模型預(yù)測值與真實(shí)值之后,該組件輸出損失值以及關(guān)于最后一層的梯度(用于梯度回傳)
          • optimizer 優(yōu)化器,負(fù)責(zé)使用梯度更新模型的參數(shù)

          然后我們還需要一些組件把上面這個(gè) 4 種基本組件整合到一起,形成一個(gè) pipeline

          • net 組件負(fù)責(zé)管理 tensor 在 layers 之間的前向和反向傳播,同時(shí)能提供獲取參數(shù)、設(shè)置參數(shù)、獲取梯度的接口
          • model 組件負(fù)責(zé)整合所有組件,形成整個(gè) pipeline。即 net 組件進(jìn)行前向傳播 -> losses 組件計(jì)算損失和梯度 -> net 組件將梯度反向傳播 -> optimizer 組件將梯度更新到參數(shù)。

          基本的框架圖如下圖

          組件實(shí)現(xiàn)

          按照上面的抽象,我們可以寫出整個(gè)流程代碼如下。

          # define modelnet = Net([layer1, layer2, ...])model = Model(net, loss_fn, optimizer)
          # trainingpred = model.forward(train_X)loss, grads = model.backward(pred, train_Y)model.apply_grad(grads)
          # inferencetest_pred = model.forward(test_X)

          首先定義 net,net 的輸入是多個(gè)網(wǎng)絡(luò)層,然后將 net、loss、optimizer 一起傳給 model。model 實(shí)現(xiàn)了 forward、backward 和 apply_grad 三個(gè)接口分別對應(yīng)前向傳播、反向傳播和參數(shù)更新三個(gè)功能。接下來我們看這里邊各個(gè)部分分別如何實(shí)現(xiàn)。

          tensor

          tensor 張量是神經(jīng)網(wǎng)絡(luò)中基本的數(shù)據(jù)單位,我們這里直接使用 numpy.ndarray 類作為 tensor 類的實(shí)現(xiàn)

          numpy.ndarray https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html

          layer

          上面流程代碼中 model 進(jìn)行 forward 和 backward,其實(shí)底層都是網(wǎng)絡(luò)層在進(jìn)行實(shí)際運(yùn)算,因此網(wǎng)絡(luò)層需要有提供 forward 和 backward 接口進(jìn)行對應(yīng)的運(yùn)算。同時(shí)還應(yīng)該將該層的參數(shù)和梯度記錄下來。先實(shí)現(xiàn)一個(gè)基類如下

          # layer.pyclass Layer(object):    def __init__(self, name):        self.name = name        self.params, self.grads = None, None
          def forward(self, inputs): raise NotImplementedError
          def backward(self, grad): raise NotImplementedError

          最基礎(chǔ)的一種網(wǎng)絡(luò)層是全連接網(wǎng)絡(luò)層,實(shí)現(xiàn)如下。forward 方法接收上層的輸入 inputs,實(shí)現(xiàn)   的運(yùn)算;backward 的方法接收來自上層的梯度,計(jì)算關(guān)于參數(shù)   和輸入的梯度,然后返回關(guān)于輸入的梯度。這三個(gè)梯度的推導(dǎo)可以見附錄,這里直接給出實(shí)現(xiàn)。w_init 和 b_init 分別是參數(shù)   和   的初始化器,這個(gè)我們在另外的一個(gè)實(shí)現(xiàn)初始化器中文件 initializer.py 去實(shí)現(xiàn),這部分不是核心部件,所以在這里不展開介紹。

          # layer.pyclass Dense(Layer):    def __init__(self, num_in, num_out,                 w_init=XavierUniformInit(),                 b_init=ZerosInit()):        super().__init__("Linear")
          self.params = { "w": w_init([num_in, num_out]), "b": b_init([1, num_out])}
          self.inputs = None
          def forward(self, inputs): self.inputs = inputs return inputs @ self.params["w"] + self.params["b"]
          def backward(self, grad): self.grads["w"] = self.inputs.T @ grad self.grads["b"] = np.sum(grad, axis=0) return grad @ self.params["w"].T

          同時(shí)神經(jīng)網(wǎng)絡(luò)中的另一個(gè)重要的部分是激活函數(shù)。激活函數(shù)可以看做是一種網(wǎng)絡(luò)層,同樣需要實(shí)現(xiàn) forward 和 backward 方法。我們通過繼承 Layer 類實(shí)現(xiàn)激活函數(shù)類,這里實(shí)現(xiàn)了最常用的 ReLU 激活函數(shù)。func 和 derivation_func 方法分別實(shí)現(xiàn)對應(yīng)激活函數(shù)的正向計(jì)算和梯度計(jì)算。

          # layer.pyclass Activation(Layer):    """Base activation layer"""    def __init__(self, name):        super().__init__(name)        self.inputs = None
          def forward(self, inputs): self.inputs = inputs return self.func(inputs)
          def backward(self, grad): return self.derivative_func(self.inputs) * grad
          def func(self, x): raise NotImplementedError
          def derivative_func(self, x): raise NotImplementedError

          class ReLU(Activation): """ReLU activation function""" def __init__(self): super().__init__("ReLU")
          def func(self, x): return np.maximum(x, 0.0)
          def derivative_func(self, x): return x > 0.0

          net

          上文提到 net 類負(fù)責(zé)管理 tensor 在 layers 之間的前向和反向傳播。forward 方法很簡單,按順序遍歷所有層,每層計(jì)算的輸出作為下一層的輸入;backward 則逆序遍歷所有層,將每層的梯度作為下一層的輸入。這里我們還將每個(gè)網(wǎng)絡(luò)層參數(shù)的梯度保存下來返回,后面參數(shù)更新需要用到。另外 net 類還實(shí)現(xiàn)了獲取參數(shù)、設(shè)置參數(shù)、獲取梯度的接口,也是后面參數(shù)更新時(shí)需要用到

          # net.pyclass Net(object):    def __init__(self, layers):        self.layers = layers
          def forward(self, inputs): for layer in self.layers: inputs = layer.forward(inputs) return inputs
          def backward(self, grad): all_grads = [] for layer in reversed(self.layers): grad = layer.backward(grad) all_grads.append(layer.grads) return all_grads[::-1]
          def get_params_and_grads(self): for layer in self.layers: yield layer.params, layer.grads
          def get_parameters(self): return [layer.params for layer in self.layers]
          def set_parameters(self, params): for i, layer in enumerate(self.layers): for key in layer.params.keys(): layer.params[key] = params[i][key]

          losses

          上文我們提到 losses 組件需要做兩件事情,給定了預(yù)測值和真實(shí)值,需要計(jì)算損失值和關(guān)于預(yù)測值的梯度。我們分別實(shí)現(xiàn)為 loss 和 grad 兩個(gè)方法,這里我們實(shí)現(xiàn)多分類回歸常用的 SoftmaxCrossEntropyLoss 損失。這個(gè)的損失 loss 和梯度 grad 的計(jì)算公式推導(dǎo)進(jìn)文末附錄,這里直接給出結(jié)果:多分類 softmax 交叉熵的損失為

          梯度稍微復(fù)雜一點(diǎn),目標(biāo)類別和非目標(biāo)類別的計(jì)算公式不同。對于目標(biāo)類別維度,其梯度為對應(yīng)維度模型輸出概率減一,對于非目標(biāo)類別維度,其梯度為對應(yīng)維度輸出概率本身。

          代碼實(shí)現(xiàn)如下

          # loss.pyclass BaseLoss(object):    def loss(self, predicted, actual):        raise NotImplementedError
          def grad(self, predicted, actual): raise NotImplementedError

          class CrossEntropyLoss(BaseLoss): def loss(self, predicted, actual): m = predicted.shape[0] exps = np.exp(predicted - np.max(predicted, axis=1, keepdims=True)) p = exps / np.sum(exps, axis=1, keepdims=True) nll = -np.log(np.sum(p * actual, axis=1)) return np.sum(nll) / m
          def grad(self, predicted, actual): m = predicted.shape[0] grad = np.copy(predicted) grad -= actual return grad / m

          optimizer

          optimizer 主要實(shí)現(xiàn)一個(gè)接口 compute_step,這個(gè)方法根據(jù)當(dāng)前的梯度,計(jì)算返回實(shí)際優(yōu)化時(shí)每個(gè)參數(shù)改變的步長。我們在這里實(shí)現(xiàn)常用的 Adam 優(yōu)化器。

          # optimizer.pyclass BaseOptimizer(object):    def __init__(self, lr, weight_decay):        self.lr = lr        self.weight_decay = weight_decay
          def compute_step(self, grads, params): step = list() # flatten all gradients flatten_grads = np.concatenate( [np.ravel(v) for grad in grads for v in grad.values()]) # compute step flatten_step = self._compute_step(flatten_grads) # reshape gradients p = 0 for param in params: layer = dict() for k, v in param.items(): block = np.prod(v.shape) _step = flatten_step[p:p+block].reshape(v.shape) _step -= self.weight_decay * v layer[k] = _step p += block step.append(layer) return step
          def _compute_step(self, grad): raise NotImplementedError
          class Adam(BaseOptimizer): def __init__(self, lr=0.001, beta1=0.9, beta2=0.999, eps=1e-8, weight_decay=0.0): super().__init__(lr, weight_decay) self._b1, self._b2 = beta1, beta2 self._eps = eps
          self._t = 0 self._m, self._v = 0, 0
          def _compute_step(self, grad): self._t += 1 self._m = self._b1 * self._m + (1 - self._b1) * grad self._v = self._b2 * self._v + (1 - self._b2) * (grad ** 2) # bias correction _m = self._m / (1 - self._b1 ** self._t) _v = self._v / (1 - self._b2 ** self._t) return -self.lr * _m / (_v ** 0.5 + self._eps)

          model

          最后 model 類實(shí)現(xiàn)了我們一開始設(shè)計(jì)的三個(gè)接口 forward、backward 和 apply_grad ,forward 直接調(diào)用 net 的 forward ,backward 中把 net 、loss、optimizer 串起來,先計(jì)算損失 loss,然后反向傳播得到梯度,然后 optimizer 計(jì)算步長,最后由 apply_grad 對參數(shù)進(jìn)行更新

          # model.pyclass Model(object):    def __init__(self, net, loss, optimizer):        self.net = net        self.loss = loss        self.optimizer = optimizer
          def forward(self, inputs): return self.net.forward(inputs)
          def backward(self, preds, targets): loss = self.loss.loss(preds, targets) grad = self.loss.grad(preds, targets) grads = self.net.backward(grad) params = self.net.get_parameters() step = self.optimizer.compute_step(grads, params) return loss, step
          def apply_grad(self, grads): for grad, (param, _) in zip(grads, self.net.get_params_and_grads()): for k, v in param.items(): param[k] += grad[k]

          整體結(jié)構(gòu)

          最后我們實(shí)現(xiàn)出來核心代碼部分文件結(jié)構(gòu)如下

          tinynn├── core│   ├── initializer.py│   ├── layer.py│   ├── loss.py│   ├── model.py│   ├── net.py│   └── optimizer.py

          其中 initializer.py 這個(gè)模塊上面沒有展開講,主要實(shí)現(xiàn)了常見的參數(shù)初始化方法(零初始化、Xavier 初始化、He 初始化等),用于給網(wǎng)絡(luò)層初始化參數(shù)。

          MNIST 例子

          框架基本搭起來后,我們找一個(gè)例子來用 tinynn 這個(gè)框架 run 起來。這個(gè)例子的基本一些配置如下

          • 數(shù)據(jù)集:MNIST(http://yann.lecun.com/exdb/mnist/
          • 任務(wù)類型:多分類
          • 網(wǎng)絡(luò)結(jié)構(gòu):三層全連接 INPUT(784) -> FC(400) -> FC(100) -> OUTPUT(10),這個(gè)網(wǎng)絡(luò)接收   的輸入,其中   是每次輸入的樣本數(shù),784 是每張   的圖像展平后的向量,輸出維度為   ,其中   是樣本數(shù),10 是對應(yīng)圖片在 10 個(gè)類別上的概率
          • 激活函數(shù):ReLU
          • 損失函數(shù):SoftmaxCrossEntropy
          • optimizer:Adam(lr=1e-3)
          • batch_size:128
          • Num_epochs:20

          這里我們忽略數(shù)據(jù)載入、預(yù)處理等一些準(zhǔn)備代碼,只把核心的網(wǎng)絡(luò)結(jié)構(gòu)定義和訓(xùn)練的代碼貼出來如下

          # example/mnist/run.pynet = Net([  Dense(784, 400),  ReLU(),  Dense(400, 100),  ReLU(),  Dense(100, 10)])model = Model(net=net, loss=SoftmaxCrossEntropyLoss(), optimizer=Adam(lr=args.lr))
          iterator = BatchIterator(batch_size=args.batch_size)evaluator = AccEvaluator()for epoch in range(num_ep): for batch in iterator(train_x, train_y): # training pred = model.forward(batch.inputs) loss, grads = model.backward(pred, batch.targets) model.apply_grad(grads) # evaluate every epoch test_pred = model.forward(test_x) test_pred_idx = np.argmax(test_pred, axis=1) test_y_idx = np.asarray(test_y) res = evaluator.evaluate(test_pred_idx, test_y_idx) print(res)

          運(yùn)行結(jié)果如下

          # tinynnEpoch 0    {'total_num': 10000, 'hit_num': 9658, 'accuracy': 0.9658}Epoch 1    {'total_num': 10000, 'hit_num': 9740, 'accuracy': 0.974}Epoch 2    {'total_num': 10000, 'hit_num': 9783, 'accuracy': 0.9783}Epoch 3    {'total_num': 10000, 'hit_num': 9799, 'accuracy': 0.9799}Epoch 4    {'total_num': 10000, 'hit_num': 9805, 'accuracy': 0.9805}Epoch 5    {'total_num': 10000, 'hit_num': 9826, 'accuracy': 0.9826}Epoch 6    {'total_num': 10000, 'hit_num': 9823, 'accuracy': 0.9823}Epoch 7    {'total_num': 10000, 'hit_num': 9819, 'accuracy': 0.9819}Epoch 8    {'total_num': 10000, 'hit_num': 9820, 'accuracy': 0.982}Epoch 9    {'total_num': 10000, 'hit_num': 9838, 'accuracy': 0.9838}Epoch 10    {'total_num': 10000, 'hit_num': 9825, 'accuracy': 0.9825}Epoch 11    {'total_num': 10000, 'hit_num': 9810, 'accuracy': 0.981}Epoch 12    {'total_num': 10000, 'hit_num': 9845, 'accuracy': 0.9845}Epoch 13    {'total_num': 10000, 'hit_num': 9845, 'accuracy': 0.9845}Epoch 14    {'total_num': 10000, 'hit_num': 9835, 'accuracy': 0.9835}Epoch 15    {'total_num': 10000, 'hit_num': 9817, 'accuracy': 0.9817}Epoch 16    {'total_num': 10000, 'hit_num': 9815, 'accuracy': 0.9815}Epoch 17    {'total_num': 10000, 'hit_num': 9835, 'accuracy': 0.9835}Epoch 18    {'total_num': 10000, 'hit_num': 9826, 'accuracy': 0.9826}Epoch 19    {'total_num': 10000, 'hit_num': 9819, 'accuracy': 0.9819}

          可以看到測試集 accuracy 隨著訓(xùn)練進(jìn)行在慢慢提升,這說明數(shù)據(jù)在框架中確實(shí)按照正確的方式進(jìn)行流動(dòng)和計(jì)算,參數(shù)得到正確的更新。為了對比下效果,我用 Tensorflow 1.13 實(shí)現(xiàn)了相同的網(wǎng)絡(luò)結(jié)構(gòu)、采用相同的采數(shù)初始化方法、優(yōu)化器配置等等,得到的結(jié)果如下

          # Tensorflow 1.13.1Epoch 0    {'total_num': 10000, 'hit_num': 9591, 'accuracy': 0.9591}Epoch 1    {'total_num': 10000, 'hit_num': 9734, 'accuracy': 0.9734}Epoch 2    {'total_num': 10000, 'hit_num': 9706, 'accuracy': 0.9706}Epoch 3    {'total_num': 10000, 'hit_num': 9756, 'accuracy': 0.9756}Epoch 4    {'total_num': 10000, 'hit_num': 9722, 'accuracy': 0.9722}Epoch 5    {'total_num': 10000, 'hit_num': 9772, 'accuracy': 0.9772}Epoch 6    {'total_num': 10000, 'hit_num': 9774, 'accuracy': 0.9774}Epoch 7    {'total_num': 10000, 'hit_num': 9789, 'accuracy': 0.9789}Epoch 8    {'total_num': 10000, 'hit_num': 9766, 'accuracy': 0.9766}Epoch 9    {'total_num': 10000, 'hit_num': 9763, 'accuracy': 0.9763}Epoch 10    {'total_num': 10000, 'hit_num': 9791, 'accuracy': 0.9791}Epoch 11    {'total_num': 10000, 'hit_num': 9773, 'accuracy': 0.9773}Epoch 12    {'total_num': 10000, 'hit_num': 9804, 'accuracy': 0.9804}Epoch 13    {'total_num': 10000, 'hit_num': 9782, 'accuracy': 0.9782}Epoch 14    {'total_num': 10000, 'hit_num': 9800, 'accuracy': 0.98}Epoch 15    {'total_num': 10000, 'hit_num': 9837, 'accuracy': 0.9837}Epoch 16    {'total_num': 10000, 'hit_num': 9811, 'accuracy': 0.9811}Epoch 17    {'total_num': 10000, 'hit_num': 9793, 'accuracy': 0.9793}Epoch 18    {'total_num': 10000, 'hit_num': 9818, 'accuracy': 0.9818}Epoch 19    {'total_num': 10000, 'hit_num': 9811, 'accuracy': 0.9811}

          可以看到兩者效果上大差不差,測試集準(zhǔn)確率都收斂到 0.982 左右,就單次的實(shí)驗(yàn)看比 Tensorflow 稍微好一點(diǎn)點(diǎn)。

          總結(jié)

          tinynn 相關(guān)的源代碼在這個(gè) repo(https://github.com/borgwang/tinynn) 里。目前支持:

          • layer :全連接層、2D 卷積層、 2D反卷積層、MaxPooling 層、Dropout 層、BatchNormalization 層、RNN 層以及 ReLU、Sigmoid、Tanh、LeakyReLU、SoftPlus 等激活函數(shù)
          • loss:SigmoidCrossEntropy、SoftmaxCrossEntroy、MSE、MAE、Huber
          • optimizer:RAam、Adam、SGD、RMSProp、Momentum 等優(yōu)化器,并且增加了動(dòng)態(tài)調(diào)節(jié)學(xué)習(xí)率 LRScheduler
          • 實(shí)現(xiàn)了 mnist(分類)、nn_paint(回歸)、DQN(強(qiáng)化學(xué)習(xí))、AutoEncoder 和 DCGAN (無監(jiān)督)等常見模型。見 tinynn/examples:https://github.com/borgwang/tinynn/tree/master/examples

          tinynn 還有很多可以繼續(xù)完善的地方受限于時(shí)間還沒有完成,筆者在空閑時(shí)間會(huì)進(jìn)行維護(hù)和更新。

          當(dāng)然 tinynn 只是一個(gè)「玩具」版本的深度學(xué)習(xí)框架,一個(gè)成熟的深度學(xué)習(xí)框架至少還需要:支持自動(dòng)求導(dǎo)、高運(yùn)算效率(靜態(tài)語言加速、支持 GPU 加速)、提供豐富的算法實(shí)現(xiàn)、提供易用的接口和詳細(xì)的文檔等等。這個(gè)小項(xiàng)目的出發(fā)點(diǎn)更多地是學(xué)習(xí),在設(shè)計(jì)和實(shí)現(xiàn) tinynn 的過程中筆者個(gè)人學(xué)習(xí)確實(shí)到了很多東西,包括如何抽象、如何設(shè)計(jì)組件接口、如何更效率的實(shí)現(xiàn)、算法的具體細(xì)節(jié)等等。對筆者而言寫這個(gè)小框架除了了解深度學(xué)習(xí)框架的設(shè)計(jì)與實(shí)現(xiàn)之外還有一個(gè)好處:后續(xù)可以在這個(gè)框架上快速地實(shí)現(xiàn)一些新的算法,新的參數(shù)初始化方法,新的優(yōu)化算法,新的網(wǎng)絡(luò)結(jié)構(gòu)設(shè)計(jì),都可以快速地在這個(gè)小框架上進(jìn)行實(shí)驗(yàn)。如果你對自己設(shè)計(jì)實(shí)現(xiàn)一個(gè)深度學(xué)習(xí)框架也感興趣,希望看完這篇文章會(huì)對你有所幫助,也歡迎大家提 PR 一起貢獻(xiàn)代碼~

          附錄: Softmax 交叉熵?fù)p失和梯度推導(dǎo)

          多分類下交叉熵?fù)p失如下式:

          其中   分別是真實(shí)值和模型預(yù)測值,   是樣本數(shù),   是類別個(gè)數(shù)。由于真實(shí)值一般為一個(gè) one-hot 向量(除了真實(shí)類別維度為 1 其他均為 0),因此上式可以化簡為

          其中   是代表真實(shí)類別,   代表第   個(gè)樣本   類的預(yù)測概率。即我們需要計(jì)算的是每個(gè)樣本在真實(shí)類別上的預(yù)測概率的對數(shù)的和,然后再取負(fù)就是交叉熵?fù)p失。接下來推導(dǎo)如何求解該損失關(guān)于模型輸出的梯度,用   表示模型輸出,在多分類中通常最后會(huì)使用 Softmax 將網(wǎng)絡(luò)的輸出歸一化為一個(gè)概率分布,則 Softmax 后的輸出為

          代入上面的損失函數(shù)

          求解   關(guān)于輸出向量   的梯度,可以將   分為目標(biāo)類別所在維度   和非目標(biāo)類別維度   。首先看目標(biāo)類別所在維度   
          再看非目標(biāo)類別所在維度

          可以看到對于目標(biāo)類別維度,其梯度為對應(yīng)維度模型輸出概率減一,對于非目標(biāo)類別維度,其梯度為對應(yīng)維度輸出概率真身。

          參考

          Deep Learning, Goodfellow, et al. (2016)
          Joel Grus - Livecoding Madness - Let's Build a Deep Learning Library
          TensorFlow Documentation
          PyTorch Documentation
              
          下載1:OpenCV-Contrib擴(kuò)展模塊中文版教程
          在「小白學(xué)視覺」公眾號后臺回復(fù):擴(kuò)展模塊中文教程,即可下載全網(wǎng)第一份OpenCV擴(kuò)展模塊教程中文版,涵蓋擴(kuò)展模塊安裝、SFM算法、立體視覺、目標(biāo)跟蹤、生物視覺、超分辨率處理等二十多章內(nèi)容。

          下載2:Python視覺實(shí)戰(zhàn)項(xiàng)目52講
          小白學(xué)視覺公眾號后臺回復(fù):Python視覺實(shí)戰(zhàn)項(xiàng)目即可下載包括圖像分割、口罩檢測、車道線檢測、車輛計(jì)數(shù)、添加眼線、車牌識別、字符識別、情緒檢測、文本內(nèi)容提取、面部識別等31個(gè)視覺實(shí)戰(zhàn)項(xiàng)目,助力快速學(xué)校計(jì)算機(jī)視覺。

          下載3:OpenCV實(shí)戰(zhàn)項(xiàng)目20講
          小白學(xué)視覺公眾號后臺回復(fù):OpenCV實(shí)戰(zhàn)項(xiàng)目20講,即可下載含有20個(gè)基于OpenCV實(shí)現(xiàn)20個(gè)實(shí)戰(zhàn)項(xiàng)目,實(shí)現(xiàn)OpenCV學(xué)習(xí)進(jìn)階。

          交流群


          歡迎加入公眾號讀者群一起和同行交流,目前有SLAM、三維視覺、傳感器、自動(dòng)駕駛、計(jì)算攝影、檢測、分割、識別、醫(yī)學(xué)影像、GAN算法競賽等微信群(以后會(huì)逐漸細(xì)分),請掃描下面微信號加群,備注:”昵稱+學(xué)校/公司+研究方向“,例如:”張三 + 上海交大 + 視覺SLAM“。請按照格式備注,否則不予通過。添加成功后會(huì)根據(jù)研究方向邀請進(jìn)入相關(guān)微信群。請勿在群內(nèi)發(fā)送廣告,否則會(huì)請出群,謝謝理解~


          瀏覽 338
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  手机操逼视频 | 伊人大香蕉在线观看 | 激情久久国产视频 | 羞羞午夜福利视频 | 人人干在线视频 |