神經(jīng)網(wǎng)絡(luò)之 CNN 與 RNN 再梳理
點擊上方“機器學(xué)習(xí)與生成對抗網(wǎng)絡(luò)”,關(guān)注星標
獲取有趣、好玩的前沿干貨!
作者? SnowMapleQueen
來源? pytorch之計算機視覺??編輯? 極市平臺
1、CNN介紹
CNN是一種利用卷積計算的神經(jīng)網(wǎng)絡(luò)。它可以通過卷積計算將原像素很大的圖片保留主要特征變成很小的像素圖片。本文以李宏毅老師ppt內(nèi)容展開具體介紹。
1.1 Why CNN for Image

①為什么引入CNN ?
圖片示意:給定一個圖片放入全連接神經(jīng)網(wǎng)絡(luò),第一個hidden layer識別這張圖片有沒有綠色出現(xiàn)?有沒有黃色出現(xiàn)?有沒有斜的條紋?第二個hidden layer結(jié)合第一個hidden layer的輸出實現(xiàn)更復(fù)雜的功能,例如:如圖如果看到直線+橫線就是框框一部分,如果看到棕色+條紋就是木紋,如果看到斜條紋+綠色就是灰色類條紋一部分。再根據(jù)第二個hidden layer輸出結(jié)果,如果某個神經(jīng)元看到蜂巢就會activate,某個神經(jīng)元如果看到人就會activate。
但是我們?nèi)绻话愕赜胒ully network(全連接)神經(jīng)網(wǎng)絡(luò)處理的話,我們會需要很多的參數(shù),例如如果input的vector是個30000維,第一個hidden layer假設(shè)是1000個神經(jīng)元,那么第一個hidden layer就會30000*1000個,數(shù)據(jù)量非常大,導(dǎo)致計算效率和準確率效果低,而引入CNN,主要就是解決我們這些問題,簡化我們神經(jīng)網(wǎng)絡(luò)架構(gòu)。因為 某些weight我們是用不到的,CNN會使用過濾手段(filter)將一些不需要的參數(shù)過濾掉,保留一些重要的參數(shù)做圖像處理。
②為什么使用比較少的參數(shù)就足夠進行圖像處理 ?
三個特性:
大部分的patterns要比整張圖片小,一個神經(jīng)元不需要去觀察整個圖片,只需要觀察圖片的一小部分就能找到一個想要的pattern,例如:給定一張圖片,第一個hidden layer的某個神經(jīng)元找圖像中的鳥的嘴,另一個神經(jīng)元找鳥的爪子。如下圖只需要看紅色框不需要觀察整張圖就可以找到鳥嘴。

不同位置的鳥嘴只需要訓(xùn)練一個識別鳥嘴的參數(shù)就Ok了,不需要分別訓(xùn)練。
我們可以采用子樣品來使圖片變小,子樣不會改變目標圖像。
1.2 CNN架構(gòu)圖

1.1節(jié)的前兩個property需要卷積計算,后一個池化層處理,具體下節(jié)介紹。
1.3 卷積層
1.3.1 重要參數(shù)

1.3.2 卷積計算
矩陣卷積計算如下:

計算如下:圖像傳入的是553的像素值,經(jīng)過padding=1的填充構(gòu)成773的像素值,卷積核是333的大小,2個卷積核,步數(shù)stride=2。注意這里的卷積核深度要核傳入過來的像素值深度相同,當(dāng)每次掃描到的藍色位置數(shù)對應(yīng)卷積核紅色位置數(shù)位置相乘后相加得到綠色位置的數(shù)據(jù)。
像素數(shù)變化為:(n+2p-f)\s = (5+2-3)\2 + 1= 3得到332的數(shù)據(jù),經(jīng)過卷積輸出的像素深度等于上一層輸入的卷積核數(shù)目。將得的結(jié)果作為池化層的輸入值。
卷積核大小選取往往是奇數(shù)。深度要和它的上一層輸出像素值深度相同,如動圖卷積核的選取是3*3*3,而輸出像素值深度=本次卷積的卷積核數(shù)。這點不要混淆。
1.3.3 卷積層與全連接層的關(guān)系
其實卷積就是將全連接層的一些weight拿掉,卷積層計算輸出的結(jié)果,其實就是全連接層的hidden layer輸出的結(jié)果。如下圖所示:

卷積沒有考慮輸入的全部特征,只和filter中經(jīng)過的特征有關(guān),如下:6*6的圖像展開成36pixel,然后下一層只和輸入層的9個pixel有關(guān),沒有連接全部,這樣就會使用很少的參數(shù),具體圖解如下:

由上圖也可發(fā)現(xiàn),計算結(jié)果3和-1不像全連接網(wǎng)絡(luò)那樣全部都需要不同的weight,而這個3和-1的部分計算的weight相同,這就是所謂的參數(shù)共享(權(quán)值共享)
1.4 池化層
根據(jù)前一個卷積計算后的矩陣,再進行池化處理(將一個框框內(nèi)的四個值合并一個值,可取最大值或者平均值),如下圖:


1.5 應(yīng)用
主要使用pytorch框架來介紹卷積神經(jīng)網(wǎng)絡(luò)。
源代碼:
torch.nn.Conv2d(
in_channels:?int,?#輸入圖像的通道數(shù)
out_channels:?int,?#卷積產(chǎn)生的輸出通道數(shù)
kernel_size:?Union[T,?Tuple[T,?T]],?#卷積核大小
stride:?Union[T,?Tuple[T,?T]]?=?1,?#卷積的步數(shù)?默認:1
padding:?Union[T,?Tuple[T,?T]]?=?0,?#0填充添加到輸入的兩邊?默認:0
dilation:?Union[T,?Tuple[T,?T]]?=?1,?#核元素之間的間距?默認:1
groups:?int?=?1,?#從輸入通道到輸出通道的阻塞鏈接數(shù),默認:1
#groups:控制輸入和輸出之間的連接,輸入和輸出通道數(shù)必須被組整除,
#當(dāng)groups=1:所有輸入移交給所有輸出
#當(dāng)groups=2:相當(dāng)于兩個卷積層,一個看到一半的輸入通道并產(chǎn)生一半的輸出通道,將兩個合并
#當(dāng)groups=in_channels:每個通道都有自己的一組過濾器,其大小為out_channel/in_channel
bias:?bool?=?True,?#將可學(xué)習(xí)的偏差添加到輸出中?默認:true
padding_mode:?str?=?'zeros')
#注:kenerl_size,stride,padding,dilation參數(shù)類型可以是int,可以是tuple,當(dāng)是tuple時,第一個int是高度維度,第二個是寬度維度。當(dāng)是單個int時,高度寬度值相同
#?方形核和等步幅
m?=?nn.Conv2d(16,?33,?3,?stride=2)
#?非方形核和不等步幅和填充
m?=?nn.Conv2d(16,?33,?(3,?5),?stride=(2,?1),?padding=(4,?2))
#?非方形核和不等步幅和填充和擴展
m?=?nn.Conv2d(16,?33,?(3,?5),?stride=(2,?1),?padding=(4,?2),?dilation=(3,?1))
input?=?torch.randn(20,?16,?50,?100)
output?=?m(input)
應(yīng)用:此處采用的是VGG16,順便介紹以下pytorch的torchsummary庫,可以把網(wǎng)絡(luò)模型打印出來,例如:
import?torchvision.models?as?models
import?torch.nn?as?nn
import?torch
from?torchsummary?import?summary
device?=?torch.device('cuda'?if?torch.cuda.is_available()?else?'cpu')
model?=?models.vgg16(pretrained=True).to(device)
print(model)
輸出:
VGG(
??(features):?Sequential(
????(0):?Conv2d(3,?64,?kernel_size=(3,?3),?stride=(1,?1),?padding=(1,?1))
????(1):?ReLU(inplace)
????(2):?Conv2d(64,?64,?kernel_size=(3,?3),?stride=(1,?1),?padding=(1,?1))
????(3):?ReLU(inplace)
????(4):?MaxPool2d(kernel_size=2,?stride=2,?padding=0,?dilation=1,?ceil_mode=False)
????(5):?Conv2d(64,?128,?kernel_size=(3,?3),?stride=(1,?1),?padding=(1,?1))
????(6):?ReLU(inplace)
????(7):?Conv2d(128,?128,?kernel_size=(3,?3),?stride=(1,?1),?padding=(1,?1))
????(8):?ReLU(inplace)
????(9):?MaxPool2d(kernel_size=2,?stride=2,?padding=0,?dilation=1,?ceil_mode=False)
????(10):?Conv2d(128,?256,?kernel_size=(3,?3),?stride=(1,?1),?padding=(1,?1))
????(11):?ReLU(inplace)
????(12):?Conv2d(256,?256,?kernel_size=(3,?3),?stride=(1,?1),?padding=(1,?1))
????(13):?ReLU(inplace)
????(14):?Conv2d(256,?256,?kernel_size=(3,?3),?stride=(1,?1),?padding=(1,?1))
????(15):?ReLU(inplace)
????(16):?MaxPool2d(kernel_size=2,?stride=2,?padding=0,?dilation=1,?ceil_mode=False)
????(17):?Conv2d(256,?512,?kernel_size=(3,?3),?stride=(1,?1),?padding=(1,?1))
????(18):?ReLU(inplace)
????(19):?Conv2d(512,?512,?kernel_size=(3,?3),?stride=(1,?1),?padding=(1,?1))
????(20):?ReLU(inplace)
????(21):?Conv2d(512,?512,?kernel_size=(3,?3),?stride=(1,?1),?padding=(1,?1))
????(22):?ReLU(inplace)
????(23):?MaxPool2d(kernel_size=2,?stride=2,?padding=0,?dilation=1,?ceil_mode=False)
????(24):?Conv2d(512,?512,?kernel_size=(3,?3),?stride=(1,?1),?padding=(1,?1))
????(25):?ReLU(inplace)
????(26):?Conv2d(512,?512,?kernel_size=(3,?3),?stride=(1,?1),?padding=(1,?1))
????(27):?ReLU(inplace)
????(28):?Conv2d(512,?512,?kernel_size=(3,?3),?stride=(1,?1),?padding=(1,?1))
????(29):?ReLU(inplace)
????(30):?MaxPool2d(kernel_size=2,?stride=2,?padding=0,?dilation=1,?ceil_mode=False)
??)
??(classifier):?Sequential(
????(0):?Linear(in_features=25088,?out_features=4096,?bias=True)
????(1):?ReLU(inplace)
????(2):?Dropout(p=0.5)
????(3):?Linear(in_features=4096,?out_features=4096,?bias=True)
????(4):?ReLU(inplace)
????(5):?Dropout(p=0.5)
????(6):?Linear(in_features=4096,?out_features=1000,?bias=True)
??)
)
model.classifier?=?nn.Sequential(
????*list(model.classifier.children())[:-1])????#?remove?last?fc?layer
print(model)
summary(model,(3,224,224))
輸出:
----------------------------------------------------------------
????????Layer?(type)???????????????Output?Shape?????????Param?#
================================================================
????????????Conv2d-1?????????[-1,?64,?224,?224]???????????1,792
??????????????ReLU-2?????????[-1,?64,?224,?224]???????????????0
????????????Conv2d-3?????????[-1,?64,?224,?224]??????????36,928
??????????????ReLU-4?????????[-1,?64,?224,?224]???????????????0
?????????MaxPool2d-5?????????[-1,?64,?112,?112]???????????????0
????????????Conv2d-6????????[-1,?128,?112,?112]??????????73,856
??????????????ReLU-7????????[-1,?128,?112,?112]???????????????0
????????????Conv2d-8????????[-1,?128,?112,?112]?????????147,584
??????????????ReLU-9????????[-1,?128,?112,?112]???????????????0
????????MaxPool2d-10??????????[-1,?128,?56,?56]???????????????0
???????????Conv2d-11??????????[-1,?256,?56,?56]?????????295,168
?????????????ReLU-12??????????[-1,?256,?56,?56]???????????????0
???????????Conv2d-13??????????[-1,?256,?56,?56]?????????590,080
?????????????ReLU-14??????????[-1,?256,?56,?56]???????????????0
???????????Conv2d-15??????????[-1,?256,?56,?56]?????????590,080
?????????????ReLU-16??????????[-1,?256,?56,?56]???????????????0
????????MaxPool2d-17??????????[-1,?256,?28,?28]???????????????0
???????????Conv2d-18??????????[-1,?512,?28,?28]???????1,180,160
?????????????ReLU-19??????????[-1,?512,?28,?28]???????????????0
???????????Conv2d-20??????????[-1,?512,?28,?28]???????2,359,808
?????????????ReLU-21??????????[-1,?512,?28,?28]???????????????0
???????????Conv2d-22??????????[-1,?512,?28,?28]???????2,359,808
?????????????ReLU-23??????????[-1,?512,?28,?28]???????????????0
????????MaxPool2d-24??????????[-1,?512,?14,?14]???????????????0
???????????Conv2d-25??????????[-1,?512,?14,?14]???????2,359,808
?????????????ReLU-26??????????[-1,?512,?14,?14]???????????????0
???????????Conv2d-27??????????[-1,?512,?14,?14]???????2,359,808
?????????????ReLU-28??????????[-1,?512,?14,?14]???????????????0
???????????Conv2d-29??????????[-1,?512,?14,?14]???????2,359,808
?????????????ReLU-30??????????[-1,?512,?14,?14]???????????????0
????????MaxPool2d-31????????????[-1,?512,?7,?7]???????????????0
???????????Linear-32?????????????????[-1,?4096]?????102,764,544
?????????????ReLU-33?????????????????[-1,?4096]???????????????0
??????????Dropout-34?????????????????[-1,?4096]???????????????0
???????????Linear-35?????????????????[-1,?4096]??????16,781,312
?????????????ReLU-36?????????????????[-1,?4096]???????????????0
??????????Dropout-37?????????????????[-1,?4096]???????????????0
================================================================
Total?params:?134,260,544
Trainable?params:?134,260,544
Non-trainable?params:?0
----------------------------------------------------------------
Input?size?(MB):?0.57
Forward/backward?pass?size?(MB):?218.58
Params?size?(MB):?512.16
Estimated?Total?Size?(MB):?731.32
----------------------------------------------------------------
2、RNN介紹
2.1 引言
每個訂票系統(tǒng)都會有一個空位填充(slot filling),有些slot是Destination,有些slot是time of arrival,系統(tǒng)要知道哪些詞屬于哪個slot;例如:
I?would?like?to?arrive?Taipei?on?November?2nd;
這里的Taipei就是Destination,November?2nd就是time?of?arrival;
采用普通的神經(jīng)網(wǎng)絡(luò),將Taipei這個詞丟入到網(wǎng)絡(luò)中,當(dāng)然在丟入之前要將其轉(zhuǎn)化成向量表示,如何表示成向量?方法很多此處采用:1-of-N Encoding,表示方式如下:

其他詞向量方式如下:

但是,如果有如下情況,系統(tǒng)就會出錯。

問: 怎么辦?使有時輸入Taipei時,輸出dest概率高,有時輸入Taipei時輸出出發(fā)地概率高?
答: 此時就要使我們的網(wǎng)絡(luò)有“記憶”,能記住前面輸入的數(shù)據(jù)。例如:Taipei是目的地時看到了arrive,Taipei是出發(fā)地時看到了leave。那么這種有記憶的網(wǎng)絡(luò)就叫做:Recurrent Neural Network(RNN)
2.2 RNN簡介
RNN的隱藏層的輸出會存儲在內(nèi)存中,當(dāng)下次輸入數(shù)據(jù)時會使用到內(nèi)存中存儲的上次的輸出。圖解如下:

圖中,同樣的weight用同樣的顏色表示。當(dāng)然hidden layer可以有很多層;以上介紹的RNN是最簡單的,接下來介紹加強版的LSTM;
2.3 RNN之LSTM
現(xiàn)在常用的內(nèi)存(memory)是Long short-term內(nèi)存。
當(dāng)外部信息需要輸入到memory時需要一個“閘門”——input gate,而input gate什么時候打開和關(guān)閉是被神經(jīng)網(wǎng)絡(luò)學(xué)到的,同理output gate也是神經(jīng)網(wǎng)絡(luò)學(xué)習(xí)的,forget gate也是如此。
所以LSTM有四個input,1個output。簡圖如下:


公式如下:

2.4 LSTM例子

圖中x2 = 1將x1送入memory中,如果x2=-1就清空memory的數(shù)值,如果x3=1,就將memory中的數(shù)據(jù)輸出。如上圖,第一列,x2=0不送入memory,第二列x2=1,將此刻x1=3送入memory中(注意memory中的數(shù)據(jù)是x1的累加,例如第四列,x2=1,此時有x1=4,memory中=3,所以一起就是7)第五列發(fā)現(xiàn)x3=1,可以輸出,所以輸出memory中的值7.
結(jié)合LSTM簡圖如下:

假設(shè)進來的是第一列:x1=3,x2=1,x3=0步驟:g—Tanh: x1w1+x2w2+x3w3 = 3f—sigmod: x1w1+x2w2+x3w3=90 sigmod后=1算好f和g后傳入input gate=3*1=3,forget gate = 1,代表不需要清0,x3=0,代表output gate鎖上,輸出的還是0。
2.5 LSTM實戰(zhàn)
pytorch中封裝好了LSTM網(wǎng)絡(luò),直接采用nn.lstm即可使用,例如
class?QstEncoder(nn.Module):
????def?__init__(self,?qst_vocab_size,?word_embed_size,?embed_size,?num_layers,?hidden_size):
????????super(QstEncoder,?self).__init__()
????????self.word2vec?=?nn.Embedding(qst_vocab_size,?word_embed_size)
????????self.tanh?=?nn.Tanh()
????????self.lstm?=?nn.LSTM(word_embed_size,?hidden_size,?num_layers)
????????self.fc?=?nn.Linear(2*num_layers*hidden_size,?embed_size)?????#?2?for?hidden?and?cell?states
????def?forward(self,?question):
????????qst_vec?=?self.word2vec(question)?????????????????????????????#?[batch_size,?max_qst_length=30,?word_embed_size=300]
????????qst_vec?=?self.tanh(qst_vec)
????????qst_vec?=?qst_vec.transpose(0,?1)?????????????????????????????#?[max_qst_length=30,?batch_size,?word_embed_size=300]
????????_,?(hidden,?cell)?=?self.lstm(qst_vec)????????????????????????#?[num_layers=2,?batch_size,?hidden_size=512]
????????qst_feature?=?torch.cat((hidden,?cell),?2)????????????????????#?[num_layers=2,?batch_size,?2*hidden_size=1024]
????????qst_feature?=?qst_feature.transpose(0,?1)?????????????????????#?[batch_size,?num_layers=2,?2*hidden_size=1024]
????????qst_feature?=?qst_feature.reshape(qst_feature.size()[0],?-1)??#?[batch_size,?2*num_layers*hidden_size=2048]
????????qst_feature?=?self.tanh(qst_feature)
????????qst_feature?=?self.fc(qst_feature)????????????????????????????#?[batch_size,?embed_size]
????????return?qst_feature
3、CNN與RNN的區(qū)別
CNN與RNN區(qū)別鏈接如下,引用了這個博客作者總結(jié),(https://blog.csdn.net/lff1208/article/details/77717149)具體如下。
DNN形成
為了克服梯度消失,ReLU、maxout等傳輸函數(shù)代替了sigmoid,形成了如今DNN的基本形式。結(jié)構(gòu)跟多層感知機一樣,如下圖所示:

我們看到全連接DNN的結(jié)構(gòu)里下層神經(jīng)元和所有上層神經(jīng)元都能夠形成連接,從而導(dǎo)致參數(shù)數(shù)量膨脹。假設(shè)輸入的是一幅像素為1K*1K的圖像,隱含層有1M個節(jié)點,光這一層就有10^12個權(quán)重需要訓(xùn)練,這不僅容易過擬合,而且極容易陷入局部最優(yōu)。
CNN形成
由于圖像中存在固有的局部模式(如人臉中的眼睛、鼻子、嘴巴等),所以將圖像處理和神將網(wǎng)絡(luò)結(jié)合引出卷積神經(jīng)網(wǎng)絡(luò)CNN。CNN是通過卷積核將上下層進行鏈接,同一個卷積核在所有圖像中是共享的,圖像通過卷積操作后仍然保留原先的位置關(guān)系。

通過一個例子簡單說明卷積神經(jīng)網(wǎng)絡(luò)的結(jié)構(gòu)。假設(shè)我們需要識別一幅彩色圖像,這幅圖像具有四個通道ARGB(透明度和紅綠藍,對應(yīng)了四幅相同大小的圖像),假設(shè)卷積核大小為100*100,共使用100個卷積核w1到w100(從直覺來看,每個卷積核應(yīng)該學(xué)習(xí)到不同的結(jié)構(gòu)特征)。
用w1在ARGB圖像上進行卷積操作,可以得到隱含層的第一幅圖像;這幅隱含層圖像左上角第一個像素是四幅輸入圖像左上角100*/100區(qū)域內(nèi)像素的加權(quán)求和,以此類推。
同理,算上其他卷積核,隱含層對應(yīng)100幅“圖像”。每幅圖像對是對原始圖像中不同特征的響應(yīng)。按照這樣的結(jié)構(gòu)繼續(xù)傳遞下去。CNN中還有max-pooling等操作進一步提高魯棒性。

注意到最后一層實際上是一個全連接層,在這個例子里,我們注意到輸入層到隱藏層的參數(shù)瞬間降低到了100100100=10^6個!這使得我們能夠用已有的訓(xùn)練數(shù)據(jù)得到良好的模型。題主所說的適用于圖像識別,正是由于CNN模型限制參數(shù)了個數(shù)并挖掘了局部結(jié)構(gòu)的這個特點。順著同樣的思路,利用語音語譜結(jié)構(gòu)中的局部信息,CNN照樣能應(yīng)用在語音識別中。
RNN形成
DNN無法對時間序列上的變化進行建模。然而,樣本出現(xiàn)的時間順序?qū)τ谧匀徽Z言處理、語音識別、手寫體識別等應(yīng)用非常重要。為了適應(yīng)這種需求,就出現(xiàn)了大家所說的另一種神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)——循環(huán)神經(jīng)網(wǎng)絡(luò)RNN。
在普通的全連接網(wǎng)絡(luò)或CNN中,每層神經(jīng)元的信號只能向上一層傳播,樣本的處理在各個時刻獨立,因此又被成為前向神經(jīng)網(wǎng)絡(luò)(Feed-forward Neural Networks)。而在RNN中,神經(jīng)元的輸出可以在下一個時間段直接作用到自身,即第i層神經(jīng)元在m時刻的輸入,除了(i-1)層神經(jīng)元在該時刻的輸出外,還包括其自身在(m-1)時刻的輸出!表示成圖就是這樣的:

為方便分析,按照時間段展開如下圖所示:

(t+1)時刻網(wǎng)絡(luò)的最終結(jié)果O(t+1)是該時刻輸入和所有歷史共同作用的結(jié)果!這就達到了對時間序列建模的目的。RNN可以看成一個在時間上傳遞的神經(jīng)網(wǎng)絡(luò),它的深度是時間的長度!正如我們上面所說,“梯度消失”現(xiàn)象又要出現(xiàn)了,只不過這次發(fā)生在時間軸上。
所以RNN存在無法解決長時依賴的問題。為解決上述問題,提出了LSTM(長短時記憶單元),通過cell門開關(guān)實現(xiàn)時間上的記憶功能,并防止梯度消失,LSTM單元結(jié)構(gòu)如下圖所示:

除了DNN、CNN、RNN、ResNet(深度殘差)、LSTM之外,還有很多其他結(jié)構(gòu)的神經(jīng)網(wǎng)絡(luò)。如因為在序列信號分析中,如果我能預(yù)知未來,對識別一定也是有所幫助的。因此就有了雙向RNN、雙向LSTM,同時利用歷史和未來的信息。
事實上,不論是哪種網(wǎng)絡(luò),他們在實際應(yīng)用中常常都混合著使用,比如CNN和RNN在上層輸出之前往往會接上全連接層,很難說某個網(wǎng)絡(luò)到底屬于哪個類別。不難想象隨著深度學(xué)習(xí)熱度的延續(xù),更靈活的組合方式、更多的網(wǎng)絡(luò)結(jié)構(gòu)將被發(fā)展出來。
簡單總結(jié)如下:

猜您喜歡:
等你著陸!【GAN生成對抗網(wǎng)絡(luò)】知識星球!
CVPR 2021 | GAN的說話人驅(qū)動、3D人臉論文匯總
CVPR 2021 | 圖像轉(zhuǎn)換 今如何?幾篇GAN論文
CVPR 2021生成對抗網(wǎng)絡(luò)GAN部分論文匯總
最新最全20篇!基于 StyleGAN 改進或應(yīng)用相關(guān)論文
附下載 | 經(jīng)典《Think Python》中文版
附下載 | 最新2020李沐《動手學(xué)深度學(xué)習(xí)》
附下載 |《TensorFlow 2.0 深度學(xué)習(xí)算法實戰(zhàn)》
