【深度学习】深度学习手写代码汇总(建议收藏,面试用)
這幾天一些同學在面試的時候,遇到了一些手寫代碼的題,因為之前都沒有準備到,所以基本上在寫的時候都有點蒙。
今天我就把一些常見的考題給大家整理下,這些題也是我之前準備面試的時候整理的,很多的代碼都是網(wǎng)上現(xiàn)有的代碼,感謝各位大佬的付出,我這里就作為一個搬運工了,把這些代碼跟我之前整理到的一些資料都給大家系統(tǒng)整理下,希望各位也可以去他們那里給個star或者贊!
首先,在這里感謝大佬們的代碼倉庫:
?https://github.com/heyxhh/nnet-numpy
https://gitee.com/bitosky/numpy_cnn
https://blog.csdn.net/csuyzt/article/details/82633051
https://github.com/yizt/numpy_neural_network
上面的大佬們,主要是使用numpy來搭建了一個神經(jīng)網(wǎng)絡,我之前也是參考這些大佬們的代碼準備的面試。這里給大家安利下他們的代碼庫,當然,需要配合下我之前的一些文章呀~
當然,還有我自己的百面計算機視覺的面經(jīng)倉庫:https://github.com/zonechen1994/CV_Interview,歡迎大佬star!
全連接層的前向與反向
首先看下全連接層的前向與反向,這里先看下之前的一篇文章。
面試必問|手撕反向傳播
import?numpy?as?np#?定義線性層網(wǎng)絡 class?Linear():"""線性全連接層"""def?__init__(self,?dim_in,?dim_out):"""參數(shù):dim_in:?輸入維度dim_out:?輸出維度"""#?初始化參數(shù)scale?=?np.sqrt(dim_in?/?2)self.weight?=?np.random.standard_normal((dim_in,?dim_out))?/?scaleself.bias?=?np.random.standard_normal(dim_out)?/?scale#?self.weight?=?np.random.randn(dim_in,?dim_out)#?self.bias?=?np.zeros(dim_out)self.params?=?[self.weight,?self.bias]def?__call__(self,?X):"""參數(shù):X:這一層的輸入,shape=(batch_size, dim_in)return:xw?+?b"""self.X?=?Xreturn?self.forward()def?forward(self):return?np.dot(self.X,?self.weight)?+?self.biasdef?backward(self,?d_out):"""參數(shù):d_out:輸出的梯度, shape=(batch_size, dim_out)return:返回loss對輸入?X?的梯度(前一層(l-1)的激活值的梯度)"""#?計算梯度#?對input的梯度有batch維度,對參數(shù)的梯度對batch維度取平均d_x?=?np.dot(d_out,?self.weight.T)??#?輸入也即上一層激活值的梯度d_w?=?np.dot(self.X.T,?d_out)??#?weight的梯度d_b?=?np.mean(d_out,?axis=0)??#?bias的梯度return?d_x,?[d_w,?d_b]
Dropout前向與反向
這里給大家分享下我之前的兩篇文章:
我丟!算法崗必問!建議收藏!
我再丟! 算法必問!
class?Dropout():"""在訓練時隨機將部分feature置為0"""def?__init__(self,?p):"""parameters:p:?保留比例"""self.p?=?pdef?__call__(self,?X,?mode):"""mode:?是在訓練階段還是測試階段.?train?或者?test"""return?self.forward(X,?mode)def?forward(self,?X,?mode):if?mode?==?'train':self.mask?=?np.random.binomial(1,?self.p,?X.shape)?/?self.pout?=??self.mask?*?Xelse:out?=?Xreturn?outdef?backward(self,?d_out):"""d_out:?loss對dropout輸出的梯度"""return?d_out?*?self.mask
激活函數(shù)之ReLu/Sigmoid/Tanh
這里給大家看下我之前關于激活函數(shù)的一些總結:
非零均值?激活函數(shù)也太硬核了!
Softmax與Sigmoid你還不知道存在這些聯(lián)系?
ReLu
import?numpy?as?np#?定義Relu層 class?Relu(object):def?__init__(self):self.X?=?Nonedef?__call__(self,?X):self.X?=?Xreturn?self.forward(self.X)def?forward(self,?X):return?np.maximum(0,?X)def?backward(self,?grad_output):"""grad_output:?loss對relu激活輸出的梯度return:?relu對輸入input_z的梯度"""grad_relu?=?self.X?>?0??#?input_z大于0的提放梯度為1,其它為0return?grad_relu?*?grad_output??#?numpy中*為點乘Tanh
class?Tanh():def?__init__(self):self.X?=?Nonedef?__call__(self,?X):self.X?=?Xreturn?self.forward(self.X)def?forward(self,?X):return?np.tanh(X)def?backward(self,?grad_output):grad_tanh?=?1?-?(np.tanh(self.X))?**?2return?grad_output?*?grad_tanhSigmoid
class?Sigmoid():def?__init__(self):self.X?=?Nonedef?__call__(self,?X):self.X?=?Xreturn?self.forward(self.X)def?forward(self,?X):return?self._sigmoid(X)def?backward(self,?grad_output):sigmoid_grad?=?self._sigmoid(self.X)?*?(1?-?self._sigmoid(self.X))return?grad_output?*?sigmoid_graddef?_sigmoid(self,?X):return?1.0?/?(1?+?np.exp(-X))卷積層前向與反向傳播
卷積在這里,如果你之前用過caffe,你就知道我們卷積是im2col來做的。這里先給出大佬的兩段代碼/
Im2Col
class?Img2colIndices():"""卷積網(wǎng)絡的滑動計算實際上是將feature map轉換成為矩陣乘法的方式。卷積計算forward前需要將feature?map轉換成為cols格式,每一次滑動的窗口作為cols的一列卷積計算backward時需要將cols態(tài)的梯度轉換成為與輸入map?shape一致的格式該輔助類完成feature?map?-->?cols?以及?cols?-->?feature?map設計卷積、maxpool、average?pool都有可能用到該類進行轉換操作"""def?__init__(self,?filter_size,?padding,?stride):"""parameters:filter_shape:?卷積核的尺寸(h_filter,?w_filter)padding:?feature邊緣填充0的個數(shù)stride:?filter滑動步幅"""self.h_filter,?self.w_filter?=?filter_sizeself.padding?=?paddingself.stride?=?stridedef?get_img2col_indices(self,?h_out,?w_out):"""獲得需要由image轉換為col的索引,?返回的索引是在feature?map填充后對于尺寸的索引獲得每次卷積時,在feature map上卷積的元素的坐標索引。以后img2col時根據(jù)索引獲得i?的每一行,如第r行是filter第r個元素(左右上下的順序)在不同位置卷積時點乘的元素的位置的row坐標索引j?的每一行,如第r行是filter第r個元素(左右上下的順序)在不同位置卷積時點乘的元素的位置的column坐標索引結果i、j每一列,如第c列是filter第c次卷積的位置卷積的k×k個元素(左右上下的順序)。每一列長filter_height*filter_width*C,由于C個通道,每C個都是重復的,表示在第幾個通道上做的卷積。parameters:h_out:?卷積層輸出feature的heightw_out:?卷積層輸出feature的width。每次調用imgcol時計算得到return:k:?shape=(filter_height*filter_width*C,?1),?每挨著的filter_height*filter_width元素值都一樣,表示從第幾個通道取點i:?shape=(filter_height*filter_width*C,?out_height*out_width),?依次待取元素的橫坐標索引j:?shape=(filter_height*filter_width*C,?out_height*out_width),?依次待取元素的縱坐標索引"""i0?=?np.repeat(np.arange(self.h_filter),?self.w_filter)i1?=?np.repeat(np.arange(h_out),?w_out)?*?self.stridei?=?i0.reshape(-1,?1)?+?i1i?=?np.tile(i,?[self.c_x,?1])j0?=?np.tile(np.arange(self.w_filter),?self.h_filter)j1?=?np.tile(np.arange(w_out),?h_out)?*?self.stridej?=?j0.reshape(-1,?1)?+?j1j?=?np.tile(j,?[self.c_x,?1])k?=?np.repeat(np.arange(self.c_x),?self.h_filter?*?self.w_filter).reshape(-1,?1)return?k,?i,?jdef?img2col(self,?X):"""基于索引取元素的方法實現(xiàn)img2colparameters:x:?輸入feature?map,shape=(batch_size,?channels,?height,?width)return:轉換img2col,shape=(h_filter?*?w_filter*chanels,?batch_size?*?h_out?*?w_out)"""self.n_x,?self.c_x,?self.h_x,?self.w_x?=?X.shape#?首先計算出輸出特征的尺寸#?計算輸出feature的尺寸,并且保證是整數(shù)h_out?=?(self.h_x?+?2?*?self.padding?-?self.h_filter)?/?self.stride?+?1w_out?=?(self.w_x?+?2?*?self.padding?-?self.w_filter)?/?self.stride?+?1if?not?h_out.is_integer()?or?not?w_out.is_integer():raise?Exception("Invalid?dimention")else:h_out,?w_out?=?int(h_out),?int(w_out)??#?上一步在進行除法后類型會是float#?0填充輸入feature?mapx_padded?=?Noneif?self.padding?>?0:x_padded?=?np.pad(X,?((0,?0),?(0,?0),?(self.padding,?self.padding),?(self.padding,?self.padding)),?mode='constant')else:x_padded?=?X#?在計算出輸出feature尺寸后,并且0填充X后,獲得img2col_indices#?img2col_indices設為實例的屬性,col2img時用,避免重復計算self.img2col_indices?=?self.get_img2col_indices(h_out,?w_out)k,?i,?j?=?self.img2col_indices#?獲得參與卷積計算的col形式cols?=?x_padded[:,?k,?i,?j]??#?shape=(batch_size,?h_filter*w_filter*n_channel,?h_out*w_out)cols?=?cols.transpose(1,?2,?0).reshape(self.h_filter?*?self.w_filter?*?self.c_x,?-1)??#?reshapereturn?colsdef?col2img(self,?cols):"""img2col的逆過程卷積網(wǎng)絡,在求出x的梯度時,dx是col矩陣的形式(filter_height*filter_width*chanels,?batch_size*out_height*out_width)將dx有col格式轉換成feature map的原尺寸格式。由get_img2col_indices獲得該尺寸下的索引,使用numpt.add.at方法還原成img格式parameters:cols:?dx的col形式,?shape=(h_filter*w_filter*n_chanels,?batch_size*h_out*w_out)"""#?將col還原成img2col的輸出shapecols?=?cols.reshape(self.h_filter?*?self.w_filter?*?self.c_x,?-1,?self.n_x)cols?=?cols.transpose(2,?0,?1)h_padded,?w_padded?=?self.h_x?+?2?*?self.padding,?self.w_x?+?2?*?self.paddingx_padded?=?np.zeros((self.n_x,?self.c_x,?h_padded,?w_padded))k,?i,?j?=?self.img2col_indicesnp.add.at(x_padded,?(slice(None),?k,?i,?j),?cols)if?self.padding?==?0:return?x_paddedelse:return?x_padded[:,?:,?self.padding?:?-self.padding,?self.padding?:?-self.padding]Conv2d前向與反向
卷積的過程,會調用im2col的函數(shù)。
class?Conv2d():def?__init__(self,?in_channels,?n_filter,?filter_size,?padding,?stride):"""parameters:in_channel:?輸入feature的通道數(shù)n_filter:?卷積核數(shù)目filter_size:?卷積核的尺寸(h_filter,?w_filter)padding:?0填充數(shù)目stride:?卷積核滑動步幅"""self.in_channels?=?in_channelsself.n_filter?=?n_filterself.h_filter,?self.w_filter?=?filter_sizeself.padding?=?paddingself.stride?=?stride#?初始化參數(shù),卷積網(wǎng)絡的參數(shù)size與輸入的size無關self.W?=?np.random.randn(n_filter,?self.in_channels,?self.h_filter,?self.w_filter)?/?np.sqrt(n_filter?/?2.)self.b?=?np.zeros((n_filter,?1))self.params?=?[self.W,?self.b]def?__call__(self,?X):#?計算輸出feature的尺寸self.n_x,?_,?self.h_x,?self.w_x?=?X.shapeself.h_out?=?(self.h_x?+?2?*?self.padding?-?self.h_filter)?/?self.stride?+?1self.w_out?=?(self.w_x?+?2?*?self.padding?-?self.w_filter)?/?self.stride?+?1if?not?self.h_out.is_integer()?or?not?self.w_out.is_integer():raise?Exception("Invalid?dimensions!")self.h_out,?self.w_out?=?int(self.h_out),?int(self.w_out)#?聲明Img2colIndices實例self.img2col_indices?=?Img2colIndices((self.h_filter,?self.w_filter),?self.padding,?self.stride)return?self.forward(X)def?forward(self,?X):#?將X轉換成colself.x_col?=?self.img2col_indices.img2col(X)#?轉換參數(shù)W的形狀,使它適合與col形態(tài)的x做計算self.w_row?=?self.W.reshape(self.n_filter,?-1)#?計算前向傳播out?=?self.w_row?@?self.x_col?+?self.b??#?@在numpy中相當于矩陣乘法,等價于numpy.matmul()out?=?out.reshape(self.n_filter,?self.h_out,?self.w_out,?self.n_x)out?=?out.transpose(3,?0,?1,?2)return?outdef?backward(self,?d_out):"""parameters:d_out:?loss對卷積輸出的梯度"""#?轉換d_out的形狀d_out_col?=?d_out.transpose(1,?2,?3,?0)d_out_col?=?d_out_col.reshape(self.n_filter,?-1)d_w?=?d_out_col?@?self.x_col.Td_w?=?d_w.reshape(self.W.shape)??#?shape=(n_filter,?d_x,?h_filter,?w_filter)d_b?=?d_out_col.sum(axis=1).reshape(self.n_filter,?1)d_x?=?self.w_row.T?@?d_out_col#?將col態(tài)的d_x轉換成image格式d_x?=?self.img2col_indices.col2img(d_x)return?d_x,?[d_w,?d_b]MaxPool2d
class?Maxpool():def?__init__(self,?size,?stride):"""parameters:size:?maxpool框框的尺寸,int類型stride:?maxpool框框的滑動步幅,一般設計步幅和size一樣"""self.size?=?size??#?maxpool框的尺寸self.stride?=?stridedef?__call__(self,?X):"""parameters:X:?輸入feature,shape=(batch_size,?channels,?height,?width)"""self.n_x,?self.c_x,?self.h_x,?self.w_x?=?X.shape#?計算maxpool輸出尺寸self.h_out?=?(self.h_x?-?self.size)?/?self.stride?+?1self.w_out?=?(self.w_x?-?self.size)?/?self.stride?+?1if?not?self.h_out.is_integer()?or?not?self.w_out.is_integer():raise?Exception("Invalid?dimensions!")self.h_out,?self.w_out?=?int(self.h_out),?int(self.w_out)#?聲明Img2colIndices實例self.img2col_indices?=?Img2colIndices((self.size,?self.size),?padding=0,?stride=self.stride)?#?maxpool不需要paddingreturn?self.forward(X)def?forward(self,?X):"""parameters:X:?輸入feature,shape=(batch_size,?channels,?height,?width)"""x_reshaped?=?X.reshape(self.n_x?*?self.c_x,?1,?self.h_x,?self.w_x)self.x_col?=?self.img2col_indices.img2col(x_reshaped)self.max_indices?=?np.argmax(self.x_col,?axis=0)out?=?self.x_col[self.max_indices,?range(self.max_indices.size)]out?=?out.reshape(self.h_out,?self.w_out,?self.n_x,?self.c_x).transpose(2,?3,?0,?1)return?outdef?backward(self,?d_out):"""parameters:d_out:?loss多maxpool輸出的梯度,shape=(batch_size,?channels,?h_out,?w_out)"""d_x_col?=?np.zeros_like(self.x_col)??#?shape=(size*size,?h_out*h_out*batch*C)d_out_flat?=?d_out.transpose(2,?3,?0,?1).ravel()d_x_col[self.max_indices,?range(self.max_indices.size)]?=?d_out_flat#?將d_x由col形態(tài)轉換到img形態(tài)d_x?=?self.img2col_indices.col2img(d_x_col)d_x?=?d_x.reshape(self.n_x,?self.c_x,?self.h_x,?self.w_x)return?d_x當然,如果不用Im2col的話,就更好理解了。在平均池化的時候,就不需要進行標記位置,可以直接用均值代替某一個區(qū)域就好了。
BatchNorm2d前向反向
這里就要安利下這篇文章了~
最全Normalization!建議收藏,面試必問!
class?BatchNorm2d():"""對卷積層來說,批量歸一化發(fā)生在卷積計算之后、應用激活函數(shù)之前。如果卷積計算輸出多個通道,我們需要對這些通道的輸出分別做批量歸一化,且每個通道都擁有獨立的拉伸和偏移參數(shù),并均為標量。設小批量中有 m 個樣本。在單個通道上,假設卷積計算輸出的高和寬分別為 p 和 q 。我們需要對該通道中 m×p×q 個元素同時做批量歸一化。對這些元素做標準化計算時,我們使用相同的均值和方差,即該通道中 m×p×q 個元素的均值和方差。將訓練好的模型用于預測時,我們希望模型對于任意輸入都有確定的輸出。因此,單個樣本的輸出不應取決于批量歸一化所需要的隨機小批量中的均值和方差。一種常用的方法是通過移動平均估算整個訓練數(shù)據(jù)集的樣本均值和方差,并在預測時使用它們得到確定的輸出。"""def?__init__(self,?n_channel,?momentum):"""parameters:n_channel:?輸入feature的通道數(shù)momentum:?moving_mean/moving_var迭代調整系數(shù)"""self.n_channel?=?n_channelself.momentum?=?momentum#?參與求梯度和迭代的拉伸和偏移參數(shù),分別初始化成1和0self.gamma?=?np.ones((1,?n_channel,?1,?1))self.beta?=?np.zeros((1,?n_channel,?1,?1))#?測試時使用的參數(shù),初始化為0,需在訓練時動態(tài)調整self.moving_mean?=?np.zeros((1,?n_channel,?1,?1))self.moving_var?=?np.zeros((1,?n_channel,?1,?1))self.params?=?[self.gamma,?self.beta]def?__call__(self,?X,?mode):"""X:?shape?=?(N,?C,?H,?W)mode:?訓練階段還是測試階段,train或test,?需要在調用時傳參"""self.X?=?X??#?求gamma的梯度時用return?self.forward(X,?mode)def?forward(self,?X,?mode):"""X:?shape?=?(N,?C,?H,?W)mode:?訓練階段還是測試階段,train或test,?需要在調用時傳參"""if?mode?!=?'train':#?如果是在預測模式下,直接使用傳入的移動平均所得的均值和方差self.x_norm?=?(X?-?self.moving_mean)?/?np.sqrt(self.moving_var?+?1e-5)else:#?使用二維卷積層的情況,計算通道維上(axis=1)的均值和方差。#?這里我們需要保持X的形狀以便后面可以做廣播運算mean?=?X.mean(axis=(0,?2,?3),?keepdims=True)self.var?=?X.var(axis=(0,?2,?3),?keepdims=True)??#?設為self,是因為backward時會用到#?訓練模式下用當前的均值和方差做標準化。設為類實例的屬性,backward時用self.x_norm?=?(X?-?mean)?/?(np.sqrt(self.var?+?1e-5))#?更新移動平均的均值和方差self.moving_mean?=?self.momentum?*?self.moving_mean?+?(1?-?self.momentum)?*?meanself.moving_var?=?self.momentum?*?self.moving_var?+?(1?-?self.momentum)?*?self.var#?拉伸和偏移out?=?self.x_norm?*?self.gamma?+?self.betareturn?outdef?backward(self,?d_out):"""d_out的形狀與輸入的形狀一樣"""d_gamma?=?(d_out?*?self.x_norm).sum(axis=(0,?2,?3),?keepdims=True)d_beta?=?d_out.sum(axis=(0,?2,?3),?keepdims=True)d_x?=?(d_out?*?self.gamma)?/?np.sqrt(self.var?+?1e-5)return?d_x,?[d_gamma,?d_beta]
Flatten層
這個層的作用,主要是進行tensor拉直的操作,方便進行全連接的進行。
class?Flatten():"""最后的卷積層輸出的feature若要連接全連接層需要將feature拉平單獨建立一個模塊是為了方便梯度反向傳播"""def?__init__(self):passdef?__call__(self,?X):self.x_shape?=?X.shape?#?(batch_size,?channels,?height,?width)return?self.forward(X)def?forward(self,?X):out?=?X.ravel().reshape(self.x_shape[0],?-1)return?outdef?backward(self,?d_out):d_x?=?d_out.reshape(self.x_shape)return?d_x損失函數(shù)
這里以交叉熵損失函數(shù)為例:
import?numpy?as?np#?交叉熵損失 class?CrossEntropyLoss():"""對最后一層的神經(jīng)元輸出計算交叉熵損失"""def?__init__(self):self.X?=?Noneself.labels?=?Nonedef?__call__(self,?X,?labels):"""參數(shù):X:?模型最后fc層輸出labels:?one?hot標注,shape=(batch_size,?num_class)"""self.X?=?Xself.labels?=?labelsreturn?self.forward(self.X)def?forward(self,?X):"""計算交叉熵損失參數(shù):X:最后一層神經(jīng)元輸出,shape=(batch_size, C)label:數(shù)據(jù)onr-hot標注,shape=(batch_size, C)return:交叉熵loss"""self.softmax_x?=?self.softmax(X)log_softmax?=?self.log_softmax(self.softmax_x)cross_entropy_loss?=?np.sum(-(self.labels?*?log_softmax),?axis=1).mean()return?cross_entropy_lossdef?backward(self):grad_x?=??(self.softmax_x?-?self.labels)??#?返回的梯度需要除以batch_sizereturn?grad_x?/?self.X.shape[0]def?log_softmax(self,?softmax_x):"""參數(shù):softmax_x,?在經(jīng)過softmax處理過的Xreturn:?log_softmax處理后的結果shape?=?(m,?C)"""return?np.log(softmax_x?+?1e-5)def?softmax(self,?X):"""根據(jù)輸入,返回softmax代碼利用softmax函數(shù)的性質:?softmax(x)?=?softmax(x?+?c)"""batch_size?=?X.shape[0]#?axis=1?表示在二維數(shù)組中沿著橫軸進行取最大值的操作max_value?=?X.max(axis=1)#每一行減去自己本行最大的數(shù)字,防止取指數(shù)后出現(xiàn)inf,性質:softmax(x)?= softmax(x + c)#?一定要新定義變量,不要用-=,否則會改變輸入X。因為在調用計算損失時,多次用到了softmax,input不能改變tmp?=?X?-?max_value.reshape(batch_size,?1)#?對每個數(shù)取指數(shù)exp_input?=?np.exp(tmp)??#?shape=(m,?n)#?求出每一行的和exp_sum?=?exp_input.sum(axis=1,?keepdims=True)??#?shape=(m,?1)return?exp_input?/?exp_sum優(yōu)化器
一文搞定面試中的優(yōu)化算法
SGD
class?SGD():"""隨機梯度下降parameters:?模型需要訓練的參數(shù)lr:?float,?學習率momentum:?float,?動量因子,默認為None不使用動量梯度下降"""def?__init__(self,?parameters,?lr,?momentum=None):self.parameters?=?parametersself.lr?=?lrself.momentum?=?momentumif?momentum?is?not?None:self.velocity?=?self.velocity_initial()def?update_parameters(self,?grads):"""grads:?調用network的backward方法,返回的grads."""if?self.momentum?==?None:for?param,?grad?in?zip(self.parameters,?grads):param?-=?self.lr?*?gradelse:for?i?in?range(len(self.parameters)):self.velocity[i]?=?self.momentum?*?self.velocity[i]?-?self.lr?*?grads[i]self.parameters[i]?+=?self.velocity[i]def?velocity_initial(self):"""初始化velocity,按照parameters的參數(shù)順序依次將v初始化為0"""velocity?=?[]for?param?in?self.parameters:velocity.append(np.zeros_like(param))return?velocity - END -作者簡介:復旦在讀博士,94年已婚有娃的前bt算法工程師。雙非材料本科出身,零基礎跨專業(yè)考研到985cs專業(yè)。
往期精彩回顧適合初學者入門人工智能的路線及資料下載機器學習及深度學習筆記等資料打印機器學習在線手冊深度學習筆記專輯《統(tǒng)計學習方法》的代碼復現(xiàn)專輯 AI基礎下載機器學習的數(shù)學基礎專輯黃海廣老師《機器學習課程》課件合集 本站qq群851320808,加入微信群請掃碼:總結
以上是生活随笔為你收集整理的【深度学习】深度学习手写代码汇总(建议收藏,面试用)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: USB外接摄像头不能用怎么办
- 下一篇: 搜狐视频怎么设置自动连播