<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>

          【小白學(xué)PyTorch】8.實戰(zhàn)之MNIST小試牛刀

          共 11771字,需瀏覽 24分鐘

           ·

          2020-09-21 18:06

          <<小白學(xué)PyTorch>>

          小白學(xué)PyTorch | 7 最新版本torchvision.transforms常用API翻譯與講解

          小白學(xué)PyTorch | 6 模型的構(gòu)建訪問遍歷存儲(附代碼)

          小白學(xué)PyTorch | 5 torchvision預(yù)訓(xùn)練模型與數(shù)據(jù)集全覽

          小白學(xué)PyTorch | 4 構(gòu)建模型三要素與權(quán)重初始化

          小白學(xué)PyTorch | 3 淺談Dataset和Dataloader

          小白學(xué)PyTorch | 2 淺談訓(xùn)練集驗證集和測試集

          小白學(xué)PyTorch | 1 搭建一個超簡單的網(wǎng)絡(luò)

          小白學(xué)PyTorch | 動態(tài)圖與靜態(tài)圖的淺顯理解


          參考目錄:

          • 1 探索性數(shù)據(jù)分析

            • 1.1 數(shù)據(jù)集基本信息

            • 1.2 數(shù)據(jù)集可視化

            • 1.3 類別是否均衡

          • 2 訓(xùn)練與推理

            • 2.1 構(gòu)建dataset

            • 2.2 構(gòu)建模型類

            • 2.3 訓(xùn)練模型

            • 2.4 推理預(yù)測

          在這個文章中,主要是來做一下MNIST手寫數(shù)字集的分類任務(wù)。這是一個基礎(chǔ)的、經(jīng)典的分類任務(wù)。建議大家一定要跟著代碼做一做,源碼和數(shù)據(jù)已經(jīng)上傳到作者公眾號“機(jī)器學(xué)習(xí)煉丹術(shù)”。回復(fù)【pytorch】獲取數(shù)據(jù)和源碼哦~

          1 探索性數(shù)據(jù)分析

          一般在進(jìn)行模型訓(xùn)練之前,都要做一個數(shù)據(jù)集分析的任務(wù)。這個在英文中一般縮寫為EDA,也就是Exploring Data Analysis(好像是這個)。

          數(shù)據(jù)集獲取方面,這里本來是要使用之前課程提到的torchvision.datasets.MNIST(),但是考慮到這個torchvision提供的MNIST完整下載下來需要200M的大小,所以我就直接提供了MNIST的數(shù)據(jù)的CSV文件(包含train.csvtest.csv),大小壓縮成.zip之后只有14M,代碼就基于了這個數(shù)據(jù)文件。

          1.1 數(shù)據(jù)集基本信息

          import?pandas?as?pd
          #?讀取訓(xùn)練集
          train_df?=?pd.read_csv('./MNIST_csv/train.csv')
          n_train?=?len(train_df)
          n_pixels?=?len(train_df.columns)?-?1
          n_class?=?len(set(train_df['label']))
          print('Number?of?training?samples:?{0}'.format(n_train))
          print('Number?of?training?pixels:?{0}'.format(n_pixels))
          print('Number?of?classes:?{0}'.format(n_class))

          #?讀取測試集
          test_df?=?pd.read_csv('./MNIST_csv/test.csv')
          n_test?=?len(test_df)
          n_pixels?=?len(test_df.columns)
          print('Number?of?test?samples:?{0}'.format(n_test))
          print('Number?of?test?pixels:?{0}'.format(n_pixels))

          輸出結(jié)果:

          訓(xùn)練集有42000個圖片,每個圖片有784個像素(所以變成圖片的話需要將784的像素變成),樣本總共有10個類別,也就是0到9。測試集中有28000個樣本。

          1.2 數(shù)據(jù)集可視化

          #?展示一些圖片
          import?numpy?as?np
          from?torchvision.utils?import?make_grid
          import?torch
          import?matplotlib.pyplot?as?plt
          random_sel?=?np.random.randint(len(train_df),?size=8)
          data?=?(train_df.iloc[random_sel,1:].values.reshape(-1,1,28,28)/255.)

          grid?=?make_grid(torch.Tensor(data),?nrow=8)
          plt.rcParams['figure.figsize']?=?(16,?2)
          plt.imshow(grid.numpy().transpose((1,2,0)))
          plt.axis('off')
          plt.show()
          print(*list(train_df.iloc[random_sel,?0].values),?sep?=?',?')

          輸出結(jié)果有一個圖片:

          以及一行打印:

          隨機(jī)挑選了8個樣本進(jìn)行可視化,然后打印出來的是樣本對應(yīng)的標(biāo)簽值。

          1.3 類別是否均衡

          然后我們需要檢查一下訓(xùn)練樣本中類別是否均衡,利用直方圖來檢查:

          #?檢查類別是否不均衡
          plt.figure(figsize=(8,5))
          plt.bar(train_df['label'].value_counts().index,?train_df['label'].value_counts())
          plt.xticks(np.arange(n_class))
          plt.xlabel('Class',?fontsize=16)
          plt.ylabel('Count',?fontsize=16)
          plt.grid('on',?axis='y')
          plt.show()

          輸出圖像:

          基本沒毛病,是均衡的。

          2 訓(xùn)練與推理

          2.1 構(gòu)建dataset

          我們可以重新寫一個python腳本,首先還是導(dǎo)入庫和讀取文件:

          import?pandas?as?pd
          train_df?=?pd.read_csv('./MNIST_csv/train.csv')
          test_df?=?pd.read_csv('./MNIST_csv/test.csv')
          n_train?=?len(train_df)
          n_test?=?len(test_df)
          n_pixels?=?len(train_df.columns)?-?1
          n_class?=?len(set(train_df['label']))

          然后構(gòu)建一個Dataset,Dataset和Dataloader的知識前面的課程已經(jīng)講過了,這里直接構(gòu)建一個:

          import?torch
          from?torch.utils.data?import?Dataset,DataLoader
          from?torchvision?import?transforms

          class?MNIST_data(Dataset):
          ????def?__init__(self,?file_path,
          ?????????????????transform=transforms.Compose([transforms.ToPILImage(),?transforms.ToTensor(),
          ???????????????????????????????????????????????transforms.Normalize(mean=(0.5,),?std=(0.5,))])

          ?????????????????)
          :

          ????????df?=?pd.read_csv(file_path)
          ????????if?len(df.columns)?==?n_pixels:
          ????????????#?test?data
          ????????????self.X?=?df.values.reshape((-1,?28,?28)).astype(np.uint8)[:,?:,?:,?None]
          ????????????self.y?=?None
          ????????else:
          ????????????#?training?data
          ????????????self.X?=?df.iloc[:,?1:].values.reshape((-1,?28,?28)).astype(np.uint8)[:,?:,?:,?None]
          ????????????self.y?=?torch.from_numpy(df.iloc[:,?0].values)
          ????????self.transform?=?transform

          ????def?__len__(self):
          ????????return?len(self.X)

          ????def?__getitem__(self,?idx):
          ????????if?self.y?is?not?None:
          ????????????return?self.transform(self.X[idx]),?self.y[idx]
          ????????else:
          ????????????return?self.transform(self.X[idx])

          可以看到,這個dataset中,根據(jù)是否有標(biāo)簽分成返回兩個不同的值。(訓(xùn)練集的話,同時返回數(shù)據(jù)和標(biāo)簽,測試集中僅僅返回數(shù)據(jù))。

          batch_size?=?64

          train_dataset?=?MNIST_data('./MNIST_csv/train.csv',
          ???????????????????????????transform=?transforms.Compose([
          ????????????????????????????transforms.ToPILImage(),
          ????????????????????????????transforms.RandomRotation(degrees=20),
          ????????????????????????????transforms.ToTensor(),
          ????????????????????????????transforms.Normalize(mean=(0.5,),?std=(0.5,))]))
          test_dataset?=?MNIST_data('./MNIST_csv/test.csv')

          train_loader?=?torch.utils.data.DataLoader(dataset=train_dataset,
          ???????????????????????????????????????????batch_size=batch_size,?shuffle=True)
          test_loader?=?torch.utils.data.DataLoader(dataset=test_dataset,
          ???????????????????????????????????????????batch_size=batch_size,?shuffle=False)

          關(guān)于這段代碼:

          • 構(gòu)建了一個train的dataset和test的dataset,然后再分別構(gòu)建對應(yīng)的dataloader
          • train_dataset中使用了隨機(jī)旋轉(zhuǎn),因為這個函數(shù)是作用在PIL圖片上的,所以需要將數(shù)據(jù)先轉(zhuǎn)成PIL再進(jìn)行旋轉(zhuǎn),然后轉(zhuǎn)成Tensor做標(biāo)準(zhǔn)化,這里標(biāo)準(zhǔn)化就隨便選取了0.5,有需要的可以做進(jìn)一步的更改。
          • 需要注意的是,轉(zhuǎn)成PIL之前的數(shù)據(jù)是numpy的格式,所以數(shù)據(jù)應(yīng)該是的形式,因為這里是單通道圖像,所以數(shù)據(jù)的shape為:(72000,28,28,1).(72000為樣本數(shù)量)
          • 像是旋轉(zhuǎn)、縮放等圖像增強(qiáng)方法在訓(xùn)練集中才會使用,這是增強(qiáng)模型訓(xùn)練難度的操作,讓模型增加魯棒性;在測試集中常規(guī)情況是不使用旋轉(zhuǎn)、縮放這樣的圖像增強(qiáng)方法的。(訓(xùn)練階段是讓模型學(xué)到內(nèi)容,測試階段主要目的是提高預(yù)測的準(zhǔn)確度,這句話感覺是廢話。。。)

          2.2 構(gòu)建模型類

          import?torch.nn?as?nn
          class?Net(nn.Module):
          ????def?__init__(self):
          ????????super(Net,?self).__init__()

          ????????self.features1?=?nn.Conv2d(1,?32,?kernel_size=3,?stride=1,?padding=1)
          ????????self.features?=?nn.Sequential(
          ????????????nn.BatchNorm2d(32),
          ????????????nn.ReLU(inplace=True),
          ????????????nn.Conv2d(32,?32,?kernel_size=3,?stride=1,?padding=1),
          ????????????nn.BatchNorm2d(32),
          ????????????nn.ReLU(inplace=True),
          ????????????nn.MaxPool2d(kernel_size=2,?stride=2),
          ????????????nn.Conv2d(32,?64,?kernel_size=3,?padding=1),
          ????????????nn.BatchNorm2d(64),
          ????????????nn.ReLU(inplace=True),
          ????????????nn.Conv2d(64,?64,?kernel_size=3,?padding=1),
          ????????????nn.BatchNorm2d(64),
          ????????????nn.ReLU(inplace=True),
          ????????????nn.MaxPool2d(kernel_size=2,?stride=2)
          ????????)

          ????????self.classifier?=?nn.Sequential(
          ????????????nn.Dropout(p=0.5),
          ????????????nn.Linear(64?*?7?*?7,?512),
          ????????????nn.BatchNorm1d(512),
          ????????????nn.ReLU(inplace=True),
          ????????????nn.Dropout(p=0.5),
          ????????????nn.Linear(512,?512),
          ????????????nn.BatchNorm1d(512),
          ????????????nn.ReLU(inplace=True),
          ????????????nn.Dropout(p=0.5),
          ????????????nn.Linear(512,?10),
          ????????)

          ????????for?m?in?self.modules():
          ????????????if?isinstance(m,?nn.Conv2d)?or?isinstance(m,?nn.Linear):
          ????????????????nn.init.xavier_uniform_(m.weight)
          ????????????elif?isinstance(m,?nn.BatchNorm2d):
          ????????????????m.weight.data.fill_(1)
          ????????????????m.bias.data.zero_()

          ????def?forward(self,?x):
          ????????x?=?self.features1(x)
          ????????x?=?self.features(x)
          ????????x?=?x.view(x.size(0),?-1)
          ????????x?=?self.classifier(x)
          ????????return?x

          這個模型類整體來看中規(guī)中矩,都是之前講到的方法。小測試:還記得xavier初始化時怎么回事嗎?xavier初始化方法是一個非常常用的方法,在之前的文章中也詳細(xì)的推導(dǎo)了這個。

          之后呢,我們對模型實例化,然后給模型的參數(shù)傳到優(yōu)化器中,然后設(shè)置一個學(xué)習(xí)率衰減的策略,學(xué)習(xí)率衰減就是訓(xùn)練的epoch越多,學(xué)習(xí)率就越低的這樣一個方法,在后面的文章中會詳細(xì)講述

          import?torch.optim?as?optim

          device?=?'cuda'?if?torch.cuda.is_available()?else?'cpu'
          model?=?Net().to(device)
          #?model?=?torchvision.models.resnet50(pretrained=True).to(device)
          optimizer?=?optim.Adam(model.parameters(),?lr=0.003)
          criterion?=?nn.CrossEntropyLoss().to(device)
          exp_lr_scheduler?=?optim.lr_scheduler.StepLR(optimizer,?step_size=7,?gamma=0.1)
          print(model)

          運行結(jié)果自然是把整個模型打印出來了:

          Net(
          ??(features1):?Conv2d(1,?32,?kernel_size=(3,?3),?stride=(1,?1),?padding=(1,?1))
          ??(features):?Sequential(
          ????(0):?BatchNorm2d(32,?eps=1e-05,?momentum=0.1,?affine=True,?track_running_stats=True)
          ????(1):?ReLU(inplace=True)
          ????(2):?Conv2d(32,?32,?kernel_size=(3,?3),?stride=(1,?1),?padding=(1,?1))
          ????(3):?BatchNorm2d(32,?eps=1e-05,?momentum=0.1,?affine=True,?track_running_stats=True)
          ????(4):?ReLU(inplace=True)
          ????(5):?MaxPool2d(kernel_size=2,?stride=2,?padding=0,?dilation=1,?ceil_mode=False)
          ????(6):?Conv2d(32,?64,?kernel_size=(3,?3),?stride=(1,?1),?padding=(1,?1))
          ????(7):?BatchNorm2d(64,?eps=1e-05,?momentum=0.1,?affine=True,?track_running_stats=True)
          ????(8):?ReLU(inplace=True)
          ????(9):?Conv2d(64,?64,?kernel_size=(3,?3),?stride=(1,?1),?padding=(1,?1))
          ????(10):?BatchNorm2d(64,?eps=1e-05,?momentum=0.1,?affine=True,?track_running_stats=True)
          ????(11):?ReLU(inplace=True)
          ????(12):?MaxPool2d(kernel_size=2,?stride=2,?padding=0,?dilation=1,?ceil_mode=False)
          ??)
          ??(classifier):?Sequential(
          ????(0):?Dropout(p=0.5,?inplace=False)
          ????(1):?Linear(in_features=3136,?out_features=512,?bias=True)
          ????(2):?BatchNorm1d(512,?eps=1e-05,?momentum=0.1,?affine=True,?track_running_stats=True)
          ????(3):?ReLU(inplace=True)
          ????(4):?Dropout(p=0.5,?inplace=False)
          ????(5):?Linear(in_features=512,?out_features=512,?bias=True)
          ????(6):?BatchNorm1d(512,?eps=1e-05,?momentum=0.1,?affine=True,?track_running_stats=True)
          ????(7):?ReLU(inplace=True)
          ????(8):?Dropout(p=0.5,?inplace=False)
          ????(9):?Linear(in_features=512,?out_features=10,?bias=True)
          ??)
          )

          2.3 訓(xùn)練模型

          def?train(epoch):
          ????model.train()
          ????for?batch_idx,?(data,?target)?in?enumerate(train_loader):
          ????????#?讀入數(shù)據(jù)
          ????????data?=?data.to(device)
          ????????target?=?target.to(device)
          ????????#?計算模型預(yù)測結(jié)果和損失
          ????????output?=?model(data)
          ????????loss?=?criterion(output,?target)

          ????????optimizer.zero_grad()?#?計算圖梯度清零
          ????????loss.backward()?#?損失反向傳播
          ????????optimizer.step()#?然后更新參數(shù)
          ????????if?(batch_idx?+?1)?%?50?==?0:
          ????????????print('Train?Epoch:?{}?[{}/{}?({:.0f}%)]\tLoss:?{:.6f}'.format(
          ????????????????epoch,?(batch_idx?+?1)?*?len(data),?len(train_loader.dataset),
          ???????????????????????100.?*?(batch_idx?+?1)?/?len(train_loader),?loss.item()))
          ???????????????
          ????exp_lr_scheduler.step()

          先定義了一個訓(xùn)練一個epoch的函數(shù),然后下面是訓(xùn)練10個epoch的主函數(shù)代碼。

          log?=?[]?#?記錄一下loss的變化情況
          n_epochs?=?2
          for?epoch?in?range(n_epochs):
          ????train(epoch)

          #?把log化成折線圖
          import?matplotlib.pyplot?as?plt
          plt.plot(log)
          plt.show()

          注意注意,這時候會報一個錯誤,我們來看一下,我詳細(xì)標(biāo)注了我個人看報錯時候的一個習(xí)慣:

          這時候我大概可以猜到,因為我們這個圖片是灰度圖片,是單通道的,可能這個RandomRotate函數(shù)要求輸入圖片是3個通道的(這個官方API上也沒有細(xì)說),怎么辦呢?完全可以直接在轉(zhuǎn)成PIL格式之前,把numpy的那個(72000,28,28,1)復(fù)制第四維度,變成(72000,28,28,3).但是這里我想用上一節(jié)課教的一個方法torchvision.transforms.GrayScale(num_output_channels), 活學(xué)活用嘛.

          所以把train_dataset那一塊改成:

          train_dataset?=?MNIST_data('./MNIST_csv/train.csv',
          ???????????????????????????transform=?transforms.Compose([
          ????????????????????????????transforms.ToPILImage(),
          ????????????????????????????transforms.Grayscale(num_output_channels=3),
          ????????????????????????????transforms.RandomRotation(degrees=20),
          ????????????????????????????transforms.ToTensor(),
          ????????????????????????????transforms.Normalize(mean=(0.5,),?std=(0.5,))]))
          test_dataset?=?MNIST_data('./MNIST_csv/test.csv',
          ??????????????????????????transform=transforms.Compose([
          ??????????????????????????????transforms.ToPILImage(),
          ??????????????????????????????transforms.Grayscale(num_output_channels=3),
          ??????????????????????????????transforms.ToTensor(),
          ??????????????????????????????transforms.Normalize(mean=(0.5,),?std=(0.5,))]))

          然后不要忘記把模型類中的第一個卷積層的輸入通道改成3哦~

          #?self.features1?=?nn.Conv2d(1,?32,?kernel_size=3,?stride=1,?padding=1)
          self.features1?=?nn.Conv2d(3,?32,?kernel_size=3,?stride=1,?padding=1)

          然后重新運行代碼,發(fā)現(xiàn)可以正常訓(xùn)練了,打印輸出的部分截圖如下:

          然后看一下?lián)p失下降的情況,算是收斂了,訓(xùn)練的epoch更多應(yīng)該會更好:發(fā)現(xiàn)訓(xùn)練是收斂的。這里需要注意的是,現(xiàn)在用全部的數(shù)據(jù)進(jìn)行訓(xùn)練,沒有使用驗證集的做法,是有可能過擬合情況出現(xiàn)的(但是這里只是訓(xùn)練了10個epoch應(yīng)該不會過擬合),更穩(wěn)妥的做法是把數(shù)據(jù)分成訓(xùn)練集和驗證機(jī)(可以是2:1,3:1,4:1)都可以,4:1比較常用,這也就是n-fold的方法。 在之后的學(xué)習(xí)中會詳細(xì)介紹這個,不過這個知識點也不難,也可以自行查閱。

          2.4 推理預(yù)測

          def?prediciton(data_loader):
          ????model.eval()
          ????test_pred?=?torch.LongTensor()

          ????for?i,?data?in?enumerate(data_loader):
          ????????data?=?data.to(device)
          ????????output?=?model(data)
          ????????pred?=?output.cpu().data.max(1,?keepdim=True)[1]
          ????????test_pred?=?torch.cat((test_pred,?pred),?dim=0)
          ????return?test_pred

          test_pred?=?prediciton(test_loader)

          類似trian,寫一個預(yù)測的函數(shù),返回預(yù)測的值。然后像是在EDA中那樣,抽取測試集的8個數(shù)字,看看圖像和預(yù)測結(jié)果的匹配情況

          from?torchvision.utils?import?make_grid
          random_sel?=?np.random.randint(len(test_df),?size=8)
          data?=?(test_df.iloc[random_sel,:].values.reshape(-1,1,28,28)/255.)

          grid?=?make_grid(torch.Tensor(data),?nrow=8)
          plt.rcParams['figure.figsize']?=?(16,?2)
          plt.imshow(grid.numpy().transpose((1,2,0)))
          plt.axis('off')
          plt.show()
          print(*list(test_pred[random_sel].numpy()),?sep?=?',?')

          輸出圖像是:打印輸出:

          OK了,恭喜你,完成了MNIST手寫數(shù)字集的分類。

          - END -




          往期精彩回顧





          獲取一折本站知識星球優(yōu)惠券,復(fù)制鏈接直接打開:

          https://t.zsxq.com/662nyZF

          本站qq群704220115。

          加入微信群請掃碼進(jìn)群(如果是博士或者準(zhǔn)備讀博士請說明):

          瀏覽 32
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <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>
                  成人无码网站在线观看 | 欧美成人黄色电影网站 | 99热免费在线观看 | 欧美乱妇高清无乱码免费-久久99国产高清 | 国产午夜视频在线观看 |