從頭搭建一個深度學習框架

從頭搭建一個深度學習框架

轉自:Build a Deep Learning Framework From Scratch

代碼:https://github.com/borgwang/tinynn

在這里插入圖片描述

當前深度學習框架越來越成熟,對于使用者而言封裝程度越來越高,好處就是現在可以非常快速地將這些框架作為工具使用,用非常少的代碼就可以進行實驗,壞處就是可能背后地實現都被隱藏起來了。在這篇文章里筆者將帶大家一起從頭設計和實現一個輕量級的(大約 200 行)、易于擴展的深度學習框架 tinynn,希望對大家了解深度學習框架的基本設計和實現有一定的幫助。

本文首先會從深度學習的流程開始分析,對神經網絡中的關鍵組件抽象,確定基本框架;然后再對框架里各個組件進行代碼實現;最后基于這個框架實現了一個 MNIST 分類的示例。

組件抽象

首先考慮神經網絡運算的流程,神經網絡運算主要包含訓練 training 和預測 predict (或 inference) 兩個階段,

  • 訓練的基本流程是:輸入數據 -> 網絡層前向傳播 -> 計算損失 -> 網絡層反向傳播梯度 -> 更新參數;
  • 預測的基本流程是 輸入數據 -> 網絡層前向傳播 -> 輸出結果。

從運算的角度看,主要可以分為三種類型的計算

  1. 數據在網絡層直接的流動

    前向傳播和反向傳播可以看做是張量 Tensor(多維數組)在網絡層之間的流動(前向傳播流動的是輸入輸出,反向傳播流動的是梯度),每個網絡層會進行一定的運算,然后將結果輸入給下一層。

  2. 計算損失

    銜接前向和反向傳播的中間過程,定義了模型的輸出與真實值之間的差異,用來后續提供反向傳播所需的信息

  3. 參數更新

    使用計算得到的梯度對網絡參數進行更新的一類計算

基于這個三種類型,我們可以對網絡的基本組件做一個抽象

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

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

  • net 組件負責管理 tensor 在 layer 之間的前向和反向傳播,同時能提供獲取參數、設置參數、獲取梯度的接口
  • model 組件負責整合所有組件,形成整個 pipeline。即 net 組件進行前向傳播 -> loss 組件計算損失和梯度 -> net 組件將梯度反向傳播 -> optimizer 組件將梯度更新到參數。

基本框架如下圖

在這里插入圖片描述

組件實現

按照上面的抽象,我們可以寫出整個流程代碼如下。首先定義 netnet 的輸入是多個網絡層,然后將 netlossoptimizer 一起傳給 modelmodel 實現了 forwardbackwardapply_grad 三個接口分別對應前向傳播、反向傳播和參數更新三個功能。

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

接下來我們看這里邊各個部分分別如何實現。

  • tensor

    tensor 張量是神經網絡中基本的數據單位,我們這里直接使用 numpy.ndarray 類作為 tensor 類的實現(numpy 底層使用了 C 和 Fortran,并且在算法層面進行了大量的優化,運算速度也不算慢)

  • layer

    上面流程代碼中 model 進行 forwardbackward,其實底層都是網絡層在進行實際運算,因此網絡層需要有提供 forwardbackward 接口進行對應的運算。同時還應該將該層的參數和梯度記錄下來。先實現一個基類如下

    # layer.py
    class Layer(object):def __init__(self, name):self.name = nameself.params, self.grads = None, Nonedef forward(self, inputs):raise NotImplementedErrordef backward(self, grad):raise NotImplementedError
    

    最基礎的一種網絡層是全連接網絡層,實現如下。forward 方法接收上層的輸入 inputs,實現 wx+bwx+bwx+b 的運算;backward 的方法接收來自上層的梯度,計算關于參數 w,bw,bw,b 和輸入的梯度,然后返回關于輸入的梯度。這三個梯度的推導可以見附錄,這里直接給出實現。w_initb_init 分別是參數 weight 和 bias 的初始化器,這個我們在另外的一個實現初始化器中文件 initializer.py 去實現,這部分不是核心部件,所以在這里不展開介紹。

    # layer.py
    class 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 = Nonedef forward(self, inputs):self.inputs = inputsreturn inputs @ self.params["w"] + self.params["b"]def backward(self, grad):self.grads["w"] = self.inputs.T @ gradself.grads["b"] = np.sum(grad, axis=0)return grad @ self.params["w"].T
    

    同時神經網絡中的另一個重要的部分是激活函數。激活函數可以看做是一種網絡層,同樣需要實現 forwardbackward 方法。我們通過繼承 Layer 類實現激活函數類,這里實現了最常用的 ReLU 激活函數。func 和 derivation_func 方法分別實現對應激活函數的正向計算和梯度計算。

    # layer.py
    class Activation(Layer):"""Base activation layer"""def __init__(self, name):super().__init__(name)self.inputs = Nonedef forward(self, inputs):self.inputs = inputsreturn self.func(inputs)def backward(self, grad):return self.derivative_func(self.inputs) * graddef func(self, x):raise NotImplementedErrordef derivative_func(self, x):raise NotImplementedErrorclass 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 類負責管理 tensor 在 layer 之間的前向和反向傳播。forward 方法很簡單,按順序遍歷所有層,每層計算的輸出作為下一層的輸入;backward 則逆序遍歷所有層,將每層的梯度作為下一層的輸入。這里我們還將每個網絡層參數的梯度保存下來返回,后面參數更新需要用到。另外 net 類還實現了獲取參數、設置參數、獲取梯度的接口,也是后面參數更新時需要用到

    # net.py
    class Net(object):def __init__(self, layers):self.layers = layersdef forward(self, inputs):for layer in self.layers:inputs = layer.forward(inputs)return inputsdef 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.gradsdef 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]
    
  • loss

    上文我們提到 loss 組件需要做兩件事情,給定了預測值和真實值,需要計算損失值和關于預測值的梯度。我們分別實現為 loss 和 grad 兩個方法,這里我們實現多分類回歸常用的 SoftmaxCrossEntropyLoss 損失。這個的損失 loss 和梯度 grad 的計算公式推導進文末附錄,這里直接給出結果:

    多分類 softmax 交叉熵的損失為
    JCE(y,y^)=?∑i=1Nlogy^icJ_{CE}(y,\hat{y})=-\sum_{i=1}^Nlog\ \hat{y}_i^c JCE?(y,y^?)=?i=1N?log?y^?ic?
    梯度稍微復雜一點,目標類別和非目標類別的計算公式不同。對于目標類別維度,其梯度為對應維度模型輸出概率減一,對于非目標類別維度,其梯度為對應維度輸出概率本身。
    ?JCE?oc={(y^c?1)/N目標類別cyc~/N非目標類別c~\frac{\partial{J_{CE}}}{\partial{o^c}}=\begin{cases} (\hat{y}^c-1)/N\ \ \ \ \ 目標類別c\\ y^{\tilde{c}}/N\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ 非目標類別\tilde{c}\end{cases} ?oc?JCE??={(y^?c?1)/N?????目標類別cyc~/N???????????????非目標類別c~?

    代碼實現如下

    # loss.py
    class BaseLoss(object):def loss(self, predicted, actual):raise NotImplementedErrordef grad(self, predicted, actual):raise NotImplementedErrorclass 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) / mdef grad(self, predicted, actual):m = predicted.shape[0]grad = np.copy(predicted)grad -= actualreturn grad / m
    
  • optimizer

    optimizer 主要實現一個接口 compute_step,這個方法根據當前的梯度,計算返回實際優化時每個參數改變的步長。我們在這里實現常用的 Adam 優化器。

    # optimizer.py
    class BaseOptimizer(object):def __init__(self, lr, weight_decay):self.lr = lrself.weight_decay = weight_decaydef compute_step(self, grads, params):step = list()# flatten all gradientsflatten_grads = np.concatenate([np.ravel(v) for grad in grads for v in grad.values()])# compute stepflatten_step = self._compute_step(flatten_grads)# reshape gradientsp = 0for 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 * vlayer[k] = _stepp += blockstep.append(layer)return stepdef _compute_step(self, grad):raise NotImplementedErrorclass 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, beta2self._eps = epsself._t = 0self._m, self._v = 0, 0def _compute_step(self, grad):self._t += 1self._m = self._b1 * self._m + (1 - self._b1) * gradself._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 類實現了我們一開始設計的三個接口 forwardbackwardapply_gradforward 直接調用 netforwardbackward 中把 netlossoptimizer 串起來,先計算損失 loss,然后反向傳播得到梯度,然后 optimizer 計算步長,最后由 apply_grad 對參數進行更新

    # model.py
    class Model(object):def __init__(self, net, loss, optimizer):self.net = netself.loss = lossself.optimizer = optimizerdef 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, stepdef 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]
    

整體結構

最后我們實現出來核心代碼部分文件結構如下:

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

其中 initializer.py 這個模塊上面沒有展開講,主要實現了常見的參數初始化方法,用于給網絡層初始化參數。

MNIST例子

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

  • 數據集:MNIST
  • 任務類型:多分類
  • 網絡結構:三層全連接 INPUT(784) -> FC(400) -> FC(100) -> OUTPUT(10),這個網絡接收 (N,784)(N,784)(N,784) 的輸入,其中 NNN 是每次輸入的樣本數,784784784 是每張 (28,28)(28,28)(28,28) 的圖像展平后的向量,輸出維度為 (N,10)(N,10)(N,10) ,其中 NNN 是樣本數,101010 是對應圖片在 101010 個類別上的概率
  • 激活函數:ReLU
  • 損失函數:SoftmaxCrossEntropy
  • optimizer:Adam(lr=1e-3)
  • batch_size:128
  • Num_epochs:20

這里我們忽略數據載入、預處理等一些準備代碼,只把核心的網絡結構定義和訓練代碼貼出來如下

# example/mnist/run.py
net = 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):# trainingpred = model.forward(batch.inputs)loss, grads = model.backward(pred, batch.targets)model.apply_grad(grads)# evaluate every epochtest_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)

運行結果如下

# tinynn
Epoch 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 隨著訓練進行在慢慢提升,這說明數據在框架中確實按照正確的方式進行流動和計算。為了對比下效果,我用 Tensorflow (1.13.1) 實現了相同的網絡結構、采用相同的采數初始化方法、優化器配置等等,得到的結果如下

# Tensorflow 1.13.1
Epoch 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}

在這里插入圖片描述

可以看到 兩者效果上大差不差,就單次的實驗看比 Tensorflow 稍微好一點點。

總結

tinynn 相關的源代碼在這個 repo 里。目前支持:

  • layer:全連接層、2D 卷積層、 2D 反卷積層、MaxPooling 層、Dropout 層、BatchNormalization 層、RNN 層以及 ReLU、Sigmoid、Tanh、LeakyReLU、SoftPlus 等激活函數
  • loss:SigmoidCrossEntropy、SoftmaxCrossEntroy、MSE、MAE、Huber
  • optimizer:RAam、Adam、SGD、RMSProp、Momentum 等優化器,并且增加了動態調節學習率 LRScheduler
  • 實現了 mnist(分類)、nn_paint(回歸)、DQN(強化學習)、AutoEncoder 和 DCGAN (無監督)等常見模型。見 tinynn/examples

tinynn 還有很多可以繼續完善的地方受限于時間還沒有完成(實現循環神經網絡層、BatchNorm 層、對運算效率進行優化等等),筆者在空閑時間會進行維護和更新。

當然從生產應用的角度 tinynn 可能是一個糟糕的選擇,理由用 python 實現在這種計算密集型的場景中會不可避免地出現性能問題、沒有 GPU 支持、沒有分布式支持、很多算法還沒實現等等等,這個小項目的出發點更多地是學習,在設計和實現 tinynn 的過程中筆者個人學習確實到了很多東西,包括如何抽象、如何設計組件接口、如何更效率的實現、算法的具體細節等等。對筆者而言這個寫這個小框架除了了解深度學習框架的設計與實現之外還有一個好處:后續可以在這個框架上快速地實現一些新的算法,比如新出了某篇 paper 提出來新的參數初始化方法,新的優化算法,新的網絡結構設計,都可以快速地在這個小框架上實驗。

如果你對自己設計實現一個深度學習框架也感興趣,希望看完這篇文章會對你有所幫助,也歡迎大家提 PR 一起貢獻代碼~

附錄 Softmax 交叉熵損失和梯度推導

多分類下交叉熵損失如下式:
JCE(y,y^)=?∑i=1N∑k=1Kyiklogyik^J_{CE}(y,\hat{y})=-\sum_{i=1}^N\sum_{k=1}^Ky_i^klog\ \hat{y_i^k} JCE?(y,y^?)=?i=1N?k=1K?yik?log?yik?^?
其中 y,y^y,\hat{y}y,y^? 分別是真實值和模型預測值,NNN 是樣本數,KKK 是類別個數。由于真實值一般為一個 one-hot 向量(除了真實類別維度為 1 其他均為 0),因此上式可以化簡為
JCE(y,y^)=?∑i=1Nlogyic^J_{CE}(y,\hat{y})=-\sum_{i=1}^Nlog\ \hat{y_i^c} JCE?(y,y^?)=?i=1N?log?yic?^?
其中 ccc 是代表真實類別,yic^\hat{y_i^c}yic?^? 代表第 iii 個樣本 ccc 類的預測概率。即我們需要計算的是每個樣本在真實類別上的預測概率的對數的和,然后再取負就是交叉熵損失。

接下來推導如何求解該損失關于模型輸出的梯度,用 ooo 表示模型輸出,在多分類中通常最后會使用 Softmax 將網絡的輸出歸一化為一個概率分布,則 Softmax 后的輸出為
yc^=exp(oc)∑k=1Kexp(ok)\hat{y_c}=\frac{exp(o^c)}{\sum_{k=1}^Kexp(o^k)} yc?^?=k=1K?exp(ok)exp(oc)?
代入上面的損失函數
JCE=?∑i=1N(oic?log∑k=1Kexp(oik))J_{CE}=-\sum_{i=1}^N(o_i^c-log\sum_{k=1}^Kexp(o_i^k)) JCE?=?i=1N?(oic??logk=1K?exp(oik?))
求解 JCEJ_{CE}JCE? 關于輸出向量 ooo 的梯度,可以將 ooo 分為目標類別所在維度 oco^coc 和非目標類別維度 KaTeX parse error: Got function '\tilde' with no arguments as superscript at position 3: o^\?t?i?l?d?e?{c}。首先看目標類別所在維度 oco^coc
?JCE?oc=?∑i=1N(1?exp(oc)∑k=1Kexp(ok))=∑i=1N(y^?1)\frac{\partial{J_{CE}}}{\partial{o^c}}=-\sum_{i=1}^N(1-\frac{exp(o^c)}{\sum_{k=1}^Kexp(o^k)})=\sum_{i=1}^N(\hat{y}-1) ?oc?JCE??=?i=1N?(1?k=1K?exp(ok)exp(oc)?)=i=1N?(y^??1)
再看非目標類別所在維度 oc~o^{\tilde{c}}oc~
?JCE?oc~=?∑i=1N(?exp(oc)∑k=1Kexp(ok))=∑i=1N(y^)\frac{\partial{J_{CE}}}{\partial{o^{\tilde{c}}}}=-\sum_{i=1}^N(-\frac{exp(o^c)}{\sum_{k=1}^Kexp(o^k)})=\sum_{i=1}^N(\hat{y}) ?oc~?JCE??=?i=1N?(?k=1K?exp(ok)exp(oc)?)=i=1N?(y^?)
可以看到對于目標類別維度,其梯度為對應維度模型輸出概率減一,對于非目標類別維度,其梯度為對應維度輸出概率本身。

參考

  • Deep Learning, Goodfellow, et al. (2016)
  • Joel Grus - Livecoding Madness - Let’s Build a Deep Learning Library
  • TensorFlow Documentation
  • PyTorch Documentation

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

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

相關文章

關于python import的sys.path路徑問題

關于python import的sys.path路徑問題 sys.path 先說一下 sys.path 這個變量,該變量需要導入 sys 官方庫方可使用,它是一個列表,是當前 python 文件 import 庫時會逐個搜索列表中的路徑。 初始化 sys.path 從這些位置初始化: …

python pdb調試基本命令整理

python pdb調試基本命令整理 使用簡介 啟動調試 侵入式 在 py 文件內部設置: import pdb; pdb.set_trace()程序會在運行到這一行時停下來,進入 pdb 交互。 非侵入式 在運行 py 腳本時: python -m pdb main.py程序會在一啟動時就進入 pdb 交…

Docker概念理解

Docker概念理解 本文非Docker命令大全,而是對Docker的概念、原理等作說明,適合有一定實操經驗后來加深理解。 轉自:docker從入門到實踐 Docker簡介 本章將帶領你進入 Docker 的世界。 什么是 Docker? 用它會帶來什么樣的好處&a…

Dockerfile詳解

Dockerfile詳解 轉自:https://yeasy.gitbook.io/docker_practice/ 使用Dockerfile定制鏡像 從剛才的 docker commit 的學習中,我們可以了解到,鏡像的定制實際上就是定制每一層所添加的配置、文件。如果我們可以把每一層修改、安裝、構建、操…

Dockerfile最佳實踐

Dockerfile最佳實踐 本文是原作者對 Docker 官方文檔中 Best practices for writing Dockerfiles 的理解與翻譯。 轉自:附錄四:Dockerfile 最佳實踐 一般性指南和建議 容器應該是短暫的 通過 Dockerfile 構建的鏡像所啟動的容器應該盡可能短暫&#xf…

Linux內存背后的那些神秘往事

Linux內存背后的那些神秘往事 作者:大白斯基(公眾號:后端研究所) 轉自:https://mp.weixin.qq.com/s/l_YdpyHht5Ayvrc7LFZNIA 前言 大家好,我的朋友們! CPU、IO、磁盤、內存可以說是影響計算機…

mmdeploy快速上手

mmdeploy快速上手 若要將使用 openmmlab 的框架(如mmdet、mmcls)等訓練的模型進行快速部署,同樣來自 openmmlab 的 mmdeploy 無疑是最合適的選擇,本文將簡單地完成一個 Faster RCNN 模型的部署。 配置 本文基于如下軟硬件配置&…

精簡CUDA教程——CUDA Driver API

精簡CUDA教程——CUDA Driver API tensorRT從零起步邁向高性能工業級部署(就業導向) 課程筆記,講師講的不錯,可以去看原視頻支持下。 Driver API概述 CUDA 的多級 API CUDA 的 API 有多級(下圖)&#xff…

CUDA編程入門極簡教程

CUDA編程入門極簡教程 轉自:CUDA編程入門極簡教程 作者:小小將 前言 2006年,NVIDIA公司發布了CUDA,CUDA是建立在NVIDIA的CPUs上的一個通用并行計算平臺和編程模型,基于CUDA編程可以利用GPUs的并行計算引擎來更加高效地…

精簡CUDA教程——CUDA Runtime API

精簡CUDA教程——CUDA Runtime API tensorRT從零起步邁向高性能工業級部署(就業導向) 課程筆記,講師講的不錯,可以去看原視頻支持下。 Runtime API 概述 環境 圖中可以看到,Runtime API 是基于 Driver API 之上開發的…

Python并發——concurrent.futures梳理

Python并發——concurrent.futures梳理 參考官方文檔: concurrent.futures — 啟動并行任務 Executor對象 class concurrent.funtures.Executor該抽象類是 ThreadPoolExecutor 和 ProcessPoolExecutor 的父類,提供異步執行調用方法。要通過它的子類調用…

TensorRT ONNX 基礎

TensorRT ONNX 基礎 tensorRT從零起步邁向高性能工業級部署(就業導向) 課程筆記,講師講的不錯,可以去看原視頻支持下。 概述 TensorRT 的核心在于對模型算子的優化(合并算子、利用當前 GPU 特性選擇特定的核函數等多種…

回文子串、回文子序列相關題目

回文子串、回文子序列相關題目 回文子串是要連續的,回文子序列可不是連續的。 516. 最長回文子序列 dp數組含義:dp[i][j]dp[i][j]dp[i][j] 表示子序列 s[i,j]s[i,j]s[i,j] 中的最長回文子序列的長度。 dp數組初始化:子序列長度為 1 時&am…

mmdetection tools工具梳理

mmdetection tools工具梳理 mmdetection 是一個非常好用的開源目標檢測框架,我們可以用它方便地訓練自己的目標檢測模型,mmdetection 項目倉庫提供許多實用的工具來實現幫助我們進行各種測試。本篇將梳理以下 mmdetection 項目倉庫 tools 目錄下的各種實…

TensorRT ONNX 基礎(續)

TensorRT ONNX 基礎(續) PyTorch正確導出ONNX 幾條推薦的原則,可以減少潛在的錯誤: 對于任何使用到 shape、size 返回值的參數時,例如 tensor.view(tensor.size(0), -1) 這類操作,避免直接使用 tensor.s…

frp實現內網穿透極簡教程

frp實現內網穿透極簡教程 本文是內網穿透極簡教程,為求簡潔,我們不介紹為什么內網穿透也不介紹其原理,這里假設各位讀者都已經明確的知道自己的目的,本文僅介紹如何安裝配置 frp 實現內網穿透。 簡單來說,內網穿透就…

圖像預處理之warpaffine與雙線性插值及其高性能實現

圖像預處理之warpaffine與雙線性插值及其高性能實現 視頻講解:https://www.bilibili.com/video/BV1ZU4y1A7EG 代碼Repo:https://github.com/shouxieai/tensorRT_Pro 本文為視頻講解的個人筆記。 warpaffine矩陣變換 對于坐標點的變換,我們通…

LeetCode-10 正則表達式匹配

LeetCode-10 正則表達式匹配 動態規劃 10. 正則表達式匹配 dp數組含義:dp[i][j]dp[i][j]dp[i][j] 表示 s[0:i?1]s[0:i-1]s[0:i?1] 能否被 p[0:j?1]p[0:j-1]p[0:j?1] 成功匹配。 狀態轉移方程 : 如果 s[i?1]p[j?1]s[i-1]p[j-1]s[i?1]p[j?1] …

shell if判斷和for循環常見寫法

shell if判斷和for循環常見寫法 轉自: Shell中for循環的幾個常用寫法 Shell中if 條件判斷總結 if常見寫法 一、if的基本語法: if [ command ];then符合該條件執行的語句 elif [ command ];then符合該條件執行的語句 else符合該條件執行的語句 fibash shell會按順序…

關于pytorch使用多個dataloader并使用zip和cycle來進行循環時出現的顯存泄漏的問題

關于pytorch使用多個dataloader并使用zip和cycle來進行循環時出現的顯存泄漏的問題 如果我們想要在 Pytorch 中同時迭代兩個 dataloader 來處理數據,會有兩種情況:一是我們按照較短的 dataloader 來迭代,長的 dataloader 超過的部分就丟棄掉…