Kaggle計(jì)算機(jī)視覺(jué)入門比賽(pytorch)

最近在kaggle上找比賽,發(fā)現(xiàn)了一個(gè)圖像入門比賽Digit Recognizer楔脯,你對(duì)R或Python和機(jī)器學(xué)習(xí)基礎(chǔ)有一些經(jīng)驗(yàn)忙芒,但你對(duì)計(jì)算機(jī)視覺(jué)還不熟悉。這個(gè)比賽旨在幫助大家熟悉計(jì)算機(jī)視覺(jué)损拢。

比賽頁(yè)面

接下來(lái)我會(huì)做一個(gè)比較完整的demo來(lái)完成這個(gè)比賽:

1.數(shù)據(jù)準(zhǔn)備

在比賽頁(yè)面的data欄可以下載到三份數(shù)據(jù)
1.sample_submission.csv 需要提交的結(jié)果文件示例
2.test.csv 測(cè)試數(shù)據(jù)
3.train.csv 訓(xùn)練數(shù)據(jù)


數(shù)據(jù)下載頁(yè)面

我們將數(shù)據(jù)下載下來(lái),假設(shè)你當(dāng)前目錄路徑為/digit_recognizer, 將數(shù)據(jù)下載到/digit_recognizer/data/ 目錄下撒犀。

2.查看數(shù)據(jù)

import pandas as pd
train_data = pd.read_csv('/digit_recognizer/data/train.csv')
test_data = pd.read_csv('/digit_recognizer/data/test.csv')
print(f'訓(xùn)練數(shù)據(jù)shape: {train_data.shape}')
print(f'測(cè)試數(shù)據(jù)shape: {test_data.shape}')
[out]:
訓(xùn)練數(shù)據(jù)shape: (42000, 785)
測(cè)試數(shù)據(jù)shape: (28000, 784)

可以看到測(cè)試集合比訓(xùn)練集少一維福压,因?yàn)橛?xùn)練數(shù)據(jù)的第0列是類標(biāo)簽(0-9)掏秩, 
手寫體數(shù)據(jù)實(shí)際上是一張28*28的矩陣,這里把這個(gè)矩陣平鋪開(kāi)了變成784維度的數(shù)據(jù)
訓(xùn)練集數(shù)據(jù)
取一張圖片看看
import matplotlib.pyplot as plt
one_img = test_data.iloc[0,:].values.reshape(28,28)
plt.imshow(one_img, cmap="Greys")
手寫體圖片

3.建模

接下來(lái)會(huì)有一個(gè)比較完整的pytorch建模過(guò)程荆姆,因?yàn)楹芎?jiǎn)單蒙幻,這里基本不需要特征工。

3.1 引入必要的包胆筒,都是一些比較常規(guī)的包
import torch
from torch import nn
from torch import optim
import torch.nn.functional as F
from torch.optim.lr_scheduler import StepLR

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split

from torchvision import transforms
from torch.utils.data import Dataset, DataLoader
3.2 加載數(shù)據(jù)
# reshape函數(shù)
class ReshapeTransform:
    def __init__(self, new_size, minmax=None):
        self.new_size = new_size
        self.minmax = minmax
    def __call__(self, img):
      if self.minmax:
        img = img/self.minmax # 這里需要縮放到0-1邮破,不然transforms.Normalize會(huì)報(bào)錯(cuò)
      img = torch.from_numpy(img)
      return torch.reshape(img, self.new_size)

# 預(yù)處理Pipeline
transform = transforms.Compose([
    ReshapeTransform((-1,28,28), 255),  # 一維向量變?yōu)?8*28圖片并且縮放(0-255)到0-1
    transforms.Normalize((0.1307,), (0.3081,)) # 均值方差標(biāo)準(zhǔn)化, (0.1307,), (0.3081,)是一個(gè)經(jīng)驗(yàn)值不必糾結(jié)

# Dataset類,配合DataLoader使用
class myDataset(Dataset):
  def __init__(self, path, transform=None, is_train=True, seed=777):
    """
    :param path:      文件路徑
    :param transform: 數(shù)據(jù)預(yù)處理
    :param train:     是否是訓(xùn)練集
    """
    self.data = pd.read_csv(path) # 讀取數(shù)據(jù)
    # 一般來(lái)說(shuō)訓(xùn)練集會(huì)分為訓(xùn)練集和驗(yàn)證集仆救,這里拆分比例為8: 2
    if is_train: 
      self.data, _ = train_test_split(self.data, train_size=0.8, random_state=seed)
    else:
      _, self.data = train_test_split(self.data, train_size=0.8, random_state=seed)
    self.transform = transform  # 數(shù)據(jù)轉(zhuǎn)化器
    self.is_train = is_train
  def __len__(self):
    # 返回data長(zhǎng)度
    return len(self.data)
  def __getitem__(self, idx):
    # 根據(jù)index返回一行
    data, lab = self.data.iloc[idx, 1:].values, self.data.iloc[idx, 0]
    if self.transform:
      data = self.transform(data)
    return data, lab
])

# 加載訓(xùn)練集
train_data = myDataset('digit_recognizer/train.csv', transform, True)
train = DataLoader(train_data, batch_size=64, shuffle=True, num_workers=4)
vail_data = myDataset('digit_recognizer/train.csv', transform, False)
vail = DataLoader(vail_data, batch_size=64, shuffle=True, num_workers=4)

# 加載測(cè)試集
test_data = pd.read_csv('digit_recognizer/test.csv')
test_data = transform(test_data.values)

到此數(shù)據(jù)已經(jīng)加載完畢抒和,一般來(lái)說(shuō)推薦使用Dataset, DataLoader類配合transforms來(lái)封裝數(shù)據(jù),transforms中轉(zhuǎn)換函數(shù)可以自己定義彤蔽,定義方法如ReshapeTransform就可以了

3.3 定義網(wǎng)絡(luò)
為了簡(jiǎn)單起見(jiàn)摧莽,這里定義一個(gè)兩層卷積,兩層全連接的網(wǎng)絡(luò)
# 初始化權(quán)重
def _weight_init(m):
    if isinstance(m, nn.Linear):
        nn.init.xavier_uniform_(m.weight)
        nn.init.constant_(m.bias, 0)
    elif isinstance(m, nn.Conv2d):
        nn.init.xavier_uniform_(m.weight)
    elif isinstance(m, nn.BatchNorm1d):
        nn.init.constant_(m.weight, 1)
        nn.init.constant_(m.bias, 0)
# 建立網(wǎng)絡(luò)
class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(1, 10, kernel_size=3)
        self.conv2 = nn.Conv2d(10, 20, kernel_size=3)
        self.drop2d = nn.Dropout2d(p=0.2)
        self.linr1 = nn.Linear(20*5*5, 32)
        self.linr2 = nn.Linear(32, 10)
        self.apply(_weight_init) # 初始化權(quán)重
    # 正向傳播 
    def forward(self, x):
        x = F.relu(self.drop2d(self.conv1(x)))
        x = F.max_pool2d(x, 2)
        x = F.relu(self.drop2d(self.conv2(x)))
        x = F.max_pool2d(x, 2)
        x = x.view(-1, 20*5*5) # 卷積接全連接需要計(jì)算好卷積輸出維度顿痪,將卷積輸出結(jié)果平鋪開(kāi)
        x = self.linr1(x)
        x = F.dropout(x,p=0.5)
        x = self.linr2(x)
        return x
net = Net()

這里使用TensorBoard畫(huà)出網(wǎng)絡(luò)圖镊辕,如下:


net網(wǎng)絡(luò)

3.4 優(yōu)化器和損失函數(shù)

# 優(yōu)化器和損失函數(shù)
optimizer = optim.Adam(net.parameters(), lr=0.0005) # 使用Adam作為優(yōu)化器
criterion = nn.CrossEntropyLoss() # 損失函數(shù)為CrossEntropyLoss,CrossEntropyLoss()=log_softmax() + NLLLoss()
scheduler = StepLR(optimizer, step_size=10, gamma=0.5) # 這里使用StepLR蚁袭,每十步學(xué)習(xí)率lr衰減50%

3.5訓(xùn)練數(shù)據(jù)

# 轉(zhuǎn)化為GPU(可選)
device = 'cuda' if torch.cuda.is_available else 'cpu'
if torch.cuda.is_available:
  net = net.to(device)
  criterion = criterion.to(device)
epochs = 100
loss_history = []
# 訓(xùn)練模型
for epoch in range(epochs):
  train_loss = []
  val_loss = []
  with torch.set_grad_enabled(True):
    net.train()
    for batch, (data, target) in enumerate(train):
      data = data.to(device).float()
      target = target.to(device)
      optimizer.zero_grad()
      predict = net(data)
      loss = criterion(predict, target)
      loss.backward()
      optimizer.step()
      train_loss.append(loss.item())
  scheduler.step() # 經(jīng)過(guò)一個(gè)epoch丑蛤,步長(zhǎng)+1
  with torch.set_grad_enabled(False):
    net.eval() # 網(wǎng)絡(luò)中有drop層,需要使用eval模式
    for batch, (data, target) in enumerate(vail):
      data = data.to(device).float()
      target = target.to(device)
      predict = net(data)
      loss = criterion(predict, target)
      val_loss.append(loss.item())
  loss_history.append([np.mean(train_loss), np.mean(val_loss)])
  print('epoch:%d train_loss: %.5f val_loss: %.5f' %(epoch+1, np.mean(train_loss), np.mean(val_loss)))
['out']:
epoch:1 train_loss: 0.96523 val_loss: 0.35177
epoch:2 train_loss: 0.37922 val_loss: 0.22583
epoch:3 train_loss: 0.28509 val_loss: 0.18644
epoch:4 train_loss: 0.24072 val_loss: 0.15961
epoch:5 train_loss: 0.20989 val_loss: 0.13630
epoch:6 train_loss: 0.19612 val_loss: 0.12432
epoch:7 train_loss: 0.17479 val_loss: 0.11251
epoch:8 train_loss: 0.16251 val_loss: 0.10917
epoch:9 train_loss: 0.15625 val_loss: 0.10470
.
.
.

現(xiàn)在模型訓(xùn)練好了我們使用TensorBoard看一下訓(xùn)練集損失和驗(yàn)證集損失撕阎,藍(lán)色為訓(xùn)練集損失,紅色為驗(yàn)證集損失


損失函數(shù)

3.6 上傳結(jié)果至kaggle

net.eval()
label = net(test_data.to(device).float().unsqueeze(1))
label = torch.argmax(label, dim=1)
submission = pd.read_csv('/digit_recognizer/sample_submission.csv')
submission.Label = label.cpu().numpy()
submission.to_csv('/digit_recognizer/version.csv', index=False)
kaggle上傳頁(yè)面

最終可以查看我們的得分

評(píng)分結(jié)果
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末碌补,一起剝皮案震驚了整個(gè)濱河市虏束,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌厦章,老刑警劉巖镇匀,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異袜啃,居然都是意外死亡汗侵,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門群发,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)晰韵,“玉大人,你說(shuō)我怎么就攤上這事熟妓⊙┲恚” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵起愈,是天一觀的道長(zhǎng)只恨。 經(jīng)常有香客問(wèn)我译仗,道長(zhǎng),這世上最難降的妖魔是什么官觅? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任纵菌,我火速辦了婚禮,結(jié)果婚禮上休涤,老公的妹妹穿的比我還像新娘咱圆。我一直安慰自己,他們只是感情好滑绒,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布闷堡。 她就那樣靜靜地躺著,像睡著了一般疑故。 火紅的嫁衣襯著肌膚如雪杠览。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,365評(píng)論 1 302
  • 那天纵势,我揣著相機(jī)與錄音踱阿,去河邊找鬼。 笑死钦铁,一個(gè)胖子當(dāng)著我的面吹牛软舌,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播牛曹,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼佛点,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了黎比?” 一聲冷哼從身側(cè)響起超营,我...
    開(kāi)封第一講書(shū)人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎阅虫,沒(méi)想到半個(gè)月后演闭,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡颓帝,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年米碰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片购城。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡吕座,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出瘪板,到底是詐尸還是另有隱情米诉,我是刑警寧澤,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布篷帅,位于F島的核電站史侣,受9級(jí)特大地震影響拴泌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜惊橱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一蚪腐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧税朴,春花似錦回季、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至觅廓,卻和暖如春鼻忠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背杈绸。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工帖蔓, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人瞳脓。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓塑娇,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親劫侧。 傳聞我的和親對(duì)象是個(gè)殘疾皇子埋酬,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354