<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12
      Title

      李宏毅ML_Spring2021HW01學(xué)習(xí)記錄

      李宏毅ML_2021Spring_HW1

      寫(xiě)在前面

      可能會(huì)有一些小錯(cuò)誤,會(huì)持續(xù)檢查和更正的


      題目如下

      Step1. 導(dǎo)入相關(guān)庫(kù)

      # 導(dǎo)入PyTorch相關(guān)庫(kù)
      import torch
      import torch.nn as nn
      from torch.utils.data import Dataset, DataLoader
      
      # 導(dǎo)入數(shù)據(jù)處理相關(guān)庫(kù)
      import numpy as np
      import csv
      import os
      
      # 導(dǎo)入繪圖相關(guān)庫(kù)
      import matplotlib.pyplot as plt
      from matplotlib.pyplot import figure
      
      myseed = 42069
      torch.backends.cudnn.deterministic = True
      torch.backends.cudnn.benchmark = False
      np.random.seed(myseed)
      torch.manual_seed(myseed)
      if torch.cuda.is_available():
          torch.cuda.manual_seed_all(myseed)
      

      這里設(shè)置隨機(jī)數(shù)種子是為了保證實(shí)驗(yàn)可復(fù)現(xiàn)性,定義一個(gè)固定的隨機(jī)種子,種子是隨機(jī)數(shù)生成器的初始值,只要種子相同,每次運(yùn)行程序時(shí)生成的隨機(jī)數(shù)序列就會(huì)完全一樣。

      torch.backends.cudnn.deterministic = True

      • 強(qiáng)制CuDNN使用確定性算法。CuDNN在某些情況下為了追求速度,可能會(huì)使用非確定性的算法(即在相同的輸入下,多次運(yùn)行的結(jié)果可能略有差異)。設(shè)置為True可以確保結(jié)果的一致性,但可能會(huì)犧牲一點(diǎn)點(diǎn)性能

      torch.backends.cudnn.benchmark = False

      • 禁用CuDNN的自動(dòng)尋找最優(yōu)卷積算法的功能。當(dāng)輸入數(shù)據(jù)的尺寸固定時(shí),CuDNN可以在第一次運(yùn)行時(shí)測(cè)試多種卷積算法,并選擇最快的一種,但這本身就是一個(gè)隨機(jī)的過(guò)程,會(huì)導(dǎo)致不確定性,將其設(shè)置為False可以確保每次都使用相同的算法

      np.random.seed(myseed)

      • 設(shè)置NumPy庫(kù)的隨機(jī)數(shù)生成器種子,影響所有使用np.random的操作

      torch.manual_seed(myseed)

      • 設(shè)置PyTorch的隨機(jī)數(shù)生成器種子,影響PyTorch中的隨機(jī)操作

      設(shè)置隨機(jī)數(shù)種子也是為了保證可重現(xiàn)性


      Step2. 一些函數(shù) (Ulitity不知道咋翻譯)

      如果GPU能用,就用GPU,不然就用CPU

      def get_device():
          return "cuda" if torch.cuda.is_available() else "cpu"
      

      繪制模型在訓(xùn)練過(guò)程中的學(xué)習(xí)曲線,即訓(xùn)練損失(train loss)和驗(yàn)證損失(dev loss)隨訓(xùn)練步數(shù)變化的趨勢(shì)。

      def plot_learning_curve(loss_record, title = ''):
          total_steps = len(loss_record['train'])
          x_1 = range(total_steps)
          x_2 = x_1[::len(loss_record['train']) // len(loss_record['dev'])]
          figure(figsize = (6, 4))
          plt.plot(x_1, loss_record['train'], c = 'tab:red', label = 'train')
          plt.plot(x_2, loss_record['dev'], c = 'tav:cyan', label = 'dev')
          plt.ylim(0.0, 5.)
          plt.xlabel('Training steps')
          plt.ylabel('MES loss')
          plt.title('Learning curve of{}'.format(title))
          plt.legend()
          plt.show()
      
      • 它接收一個(gè)名為loss_record的字典,這個(gè)字典存了兩個(gè)列表:一個(gè)是每一步(batch)訓(xùn)練的損失(train loss),另一個(gè)是每一輪(epoch)訓(xùn)練結(jié)束后的驗(yàn)證損失(dev loss)
      • 它為訓(xùn)練步驟創(chuàng)建一個(gè)x軸x_1
      • x_2 = x_1[::len(loss_record['train'])] // len(loss_record['dev'])是一個(gè)小技巧。因?yàn)轵?yàn)證損失的記錄頻率遠(yuǎn)(一個(gè)epoch)低于訓(xùn)練損失(一個(gè)batch)(訓(xùn)練時(shí)每批數(shù)據(jù)都記,驗(yàn)證時(shí)跑完一整輪才記一次),這行代碼能巧妙地在x軸上找到對(duì)應(yīng)的位置來(lái)畫(huà)驗(yàn)證損失的點(diǎn),確保兩條線能夠?qū)R。

      評(píng)估模型的最終預(yù)測(cè)能力,通過(guò)畫(huà)散點(diǎn)圖的方式將模型的“預(yù)測(cè)值”與“真實(shí)值”進(jìn)行比較

      def plot_pred(dv_set, model, device, lim = 35, preds = None, targets = None):
          if preds is None or targets is None:
              model.eval()
              preds, targets = [], []
              for x, y in dv_set:
                  x, y = x.to(device), y.to(device)
                  with torch.no_grad():
                      pred = model(x)
                      preds.append(pred.detach().cpu())
                      targets.append(y.detach().cpu())
              preds = torch.cat(preds, dim = 0).numpy()
              targets = torch.cat(targets, dim = 0).numpy()
          
          figure(figsize = (5, 5))
          plt.scatter(targets, preds, c = 'r', alpha = 0.5)
          plt.plot([-0.2, lim], [-0.2, lim], c = 'b') # 直線 y = x
          plt.xlim(-0.2, lim)
          plt.ylim(-0.2, lim)
          plt.xlabel('ground truth value')
          plt.ylabel('predicted value')
          plt.title('Ground Truth v.s. Prediction')
          plt.show()
      
      • moedl.eval():將模型切換到"評(píng)估模式",這會(huì)關(guān)閉一些只在訓(xùn)練時(shí)才開(kāi)啟的功能(比如Dropout)
      • with torch.no_grad():告訴PyTorch在這個(gè)代碼塊中不要計(jì)算梯度,因?yàn)槲覀兪窃谧鲱A(yù)測(cè),不是在訓(xùn)練,這樣做可以節(jié)省計(jì)算資源和內(nèi)存,運(yùn)行得更快。不然的話PyTorch會(huì)繼續(xù)構(gòu)建計(jì)算圖累加到原來(lái)的結(jié)果上,就糟糕了
      • 它會(huì)遍歷驗(yàn)證集(dv_set)的每一個(gè)批次(batch),用模型(model)去進(jìn)行預(yù)測(cè)
      • x, y = x.to(device), y.to(device)將數(shù)據(jù)和標(biāo)簽移動(dòng)到指定的計(jì)算設(shè)備上
      • preds.append(pred.detach().cpu()): 將預(yù)測(cè)結(jié)果從計(jì)算設(shè)備(GPU)移回 CPU,并從計(jì)算圖中分離(detach()),然后存入列表。
      • targets.append(y.detach().cpu()): 同樣處理真實(shí)標(biāo)簽。
      • torch.cat(..., dim=0).numpy(): 將所有批次的預(yù)測(cè)值和真實(shí)值拼接成一個(gè)大的張量,然后轉(zhuǎn)換為 NumPy 數(shù)組,以便 Matplotlib 繪圖。
      • 然后,會(huì)用plt.scatter()繪制散點(diǎn)圖
        • x軸代表真實(shí)值
        • y軸代表模型的預(yù)測(cè)值
      • 同時(shí)會(huì)畫(huà)一條呈45度角的藍(lán)色對(duì)角線,這條線代表"完美預(yù)測(cè)"(預(yù)測(cè)值 = 真實(shí)值)

      Step3. 預(yù)處理

      我們已經(jīng)有了數(shù)據(jù)集了:

      • train:訓(xùn)練集
      • dev:驗(yàn)證集
      • test:測(cè)試集

      Dataset -- 圖書(shū)管理員

      class COVID19Dataset(Dataset):
      
          def __init__(self, path, mode = 'train', target_only = False):
              self.mode = mode
          
              # 把數(shù)據(jù)讀到NumPy的arrays中
              with open(path, 'r') as fp:
                  data = list(csv.reader(fp))
                  data = np.array(data[1:])[:, 1:].astype(float)
              
              if not target_only:
                  feats = list(range(93))
              else:
                  # 使用 40 個(gè)州的特征,以及索引為 57 和 75 的兩個(gè)tested_positive特征
                  feats = list(range(40)) + [57, 75]
      
              if mode == 'test':
                  # 測(cè)試數(shù)據(jù) Testing data
                  # data: 893 x 93 40個(gè)列用one-hot表示州 day1(18), day2(18), day3(17)
                  data = data[:, feats]
                  self.data = torch.FloatTensor(data)
              
              else:
                  # 訓(xùn)練數(shù)據(jù) Training data (train/dev sets)
                  # data: 2700 x 94 day1(18), day2(18), day3(18) 
                  target = data[:, -1]
                  data = data[:, feats]
                  
                  # 分割訓(xùn)練數(shù)據(jù)為訓(xùn)練集和驗(yàn)證集
                  if mode == 'train':
                      indices = [i for i in range(len(data)) if i % 10 != 0]
                  elif mode == 'dev':
                      indices = [i for i in range(len(data)) if i % 10 == 0]
                  
                  # 把數(shù)據(jù)變成PyTorch的tensors
                  self.data = torch.FloatTensor(data[indices])
                  self.target = torch.FloatTensor(target[indices])
      
                  #數(shù)據(jù)歸一化(有的Error Surface梯度下降可能比較困難,歸一化使得訓(xùn)練更加順利)
                  self.data[:, 40:] = (self.data[:, 40:] - self.data[:, 40:].mean(dim = 0, keepdim = True)) / self.data[:, 40:].std(dim = 0, keepdim = True)
                  
                  self.dim = self.data.shape[1]
      
              
              def __getitem__(self, index):
      
                  if self.mode in ['train', 'dev']:
                      # 訓(xùn)練
                      return self.data[index], self.target[index]
                  else:
                      # 測(cè)試
                      return self.data[index]
              
              def __len__(self):
                  # 
                  return len(self.data)
      

      注意
      Dataset是一個(gè)抽象類(lèi),不能創(chuàng)造實(shí)例,只能用來(lái)繼承
      圖書(shū)管理員從來(lái)都不是特定的某個(gè)人,只能得到對(duì)應(yīng)的職權(quán)

      讀取和解析csv文件

      path:csv文件的路徑
      mode:數(shù)據(jù)集的模式,有三種可能的值:train訓(xùn)練集 ,dev驗(yàn)證集,test測(cè)試集,默認(rèn)為train
      target_only:一個(gè)布爾值,用于決定是否只使用部分特定的特征,還是使用全部特征

      with open(path, 'r') as fp:python打開(kāi)指定文件的方式,with語(yǔ)句可以確保文件在操作結(jié)束后被正確關(guān)閉

      data = np.array(data[1:])[:, 1:].astype(float)跳過(guò)第0行和第0列,因?yàn)榈?code>0行是表頭不是數(shù)據(jù),第0列是樣本id不是特征信息,然后讀進(jìn)來(lái)的數(shù)據(jù)是字符類(lèi)型,轉(zhuǎn)換一下方便后續(xù)計(jì)算

      特征選擇

      如果使用全部特征,就把93個(gè)特征列全列進(jìn)來(lái),不然的話,就按照題目要求使用前40個(gè)州的特征以及索引為57和75的兩個(gè)tested_positive特征

      區(qū)分不同模式(訓(xùn)練,驗(yàn)證,測(cè)試)

      對(duì)于測(cè)試集

      data = data[:, feats]
      self.data = torch.FloatTensor(data)
      

      這里我們把需要的列取出來(lái)并把它從np.ndarry轉(zhuǎn)換成tensors

      對(duì)于訓(xùn)練/驗(yàn)證集

      target = data[:, -1]
      data = data[:, feats]
      

      target就是我們理想的輸出值
      data同上,選出用于訓(xùn)練的特征列

      劃分?jǐn)?shù)據(jù)集(train)和驗(yàn)證集(dev)

      if mode == 'train':
          indices = [i for i in range(len(data)) if i % 10 != 0]
      elif mode == 'dev':
          indices = [i for i in range(len(data)) if i % 10 == 0]
      
      self.data = torch.FloatTensor(data[indices])
      self.target = torch.FloatTensor(target[indices])
      

      這里沒(méi)有直接分割data數(shù)組,而是通過(guò)生成索引indices列表的方式來(lái)分割數(shù)據(jù)

      • i % 10 != 0: i % 10 是求 i 除以 10 的余數(shù)。這個(gè)條件的意思是“如果行號(hào)不能被 10 整除”。所以,行號(hào)為 0-8, 10-18, 20-28... 的數(shù)據(jù)會(huì)被選為訓(xùn)練集(每 10 條里選 9 條)。

      • i % 10 == 0: 相反,行號(hào)為 0, 10, 20... 的數(shù)據(jù)會(huì)被選為驗(yàn)證集(每 10 條里選 1 條)。

      data[indices]: 最后,用這個(gè) indices 列表一次性地從 datatarget 中取出所有對(duì)應(yīng)的行,完成數(shù)據(jù)集的切分。

      數(shù)據(jù)標(biāo)準(zhǔn)化

      • 切片 self.data[:, 40:]: 這里的 40:` 表示“從第 40 列開(kāi)始,取到最后一列”。為什么從 40 開(kāi)始?因?yàn)楦鶕?jù)數(shù)據(jù)描述,前 40 列是代表不同州的特征(one-hot 編碼),它們的值只有 0 或 1,不需要標(biāo)準(zhǔn)化。而后面的列是數(shù)值型特征,數(shù)值范圍可能很大,需要標(biāo)準(zhǔn)化。

      • 公式 (x - mean) / std: 這就是標(biāo)準(zhǔn)的 Z-score 標(biāo)準(zhǔn)化公式。它會(huì)把數(shù)據(jù)的均值變?yōu)?0,標(biāo)準(zhǔn)差變?yōu)?1,使得所有特征都在一個(gè)相似的尺度上。

        • .mean(dim=0): 沿著列(維度0)的方向計(jì)算每一列的平均值。

        • .std(dim=0): 沿著列的方向計(jì)算每一列的標(biāo)準(zhǔn)差。

      def __getitem__(self, index)

      • 作用:根據(jù)DataLoader傳過(guò)來(lái)的一個(gè)具體的索引號(hào)(index),從數(shù)據(jù)集中取出那一條對(duì)應(yīng)的數(shù)據(jù)
      • 根據(jù)這個(gè)代碼,DataLoader會(huì)傳給它一個(gè)數(shù)字index, 如0, 1, 2...
      • 如果當(dāng)前是train或者dev模式,模型需要數(shù)據(jù)(問(wèn)題)和標(biāo)簽(答案)來(lái)進(jìn)行學(xué)習(xí)和評(píng)估,所以它會(huì)返回一個(gè)元組(tuple),包含兩條信息self.data[index]self.target[index](第index條的特征數(shù)據(jù)和第index條對(duì)應(yīng)的目標(biāo)值)
      • 如果是test模式,我們只有數(shù)據(jù),沒(méi)有標(biāo)簽,因?yàn)榇鸢甘切枰P腿ヮA(yù)測(cè)的,所以只返回self.data[index]

      def __len__(self)

      • __init__方法中,我們已經(jīng)把所有處理好的數(shù)據(jù)都存放在self.data這個(gè)變量里了。
      • len(self.data)就可以返回樣本總數(shù)(也就是行數(shù))

      DataLoader --高效的數(shù)據(jù)搬運(yùn)工

      Dataset 就像是整個(gè)圖書(shū)館的藏書(shū)清單和圖書(shū)管理員。它知道總共有多少本書(shū) (__len__),并且你告訴它書(shū)號(hào) (index),它就能幫你準(zhǔn)確地把那一本書(shū)取出來(lái) (__getitem__)

      但是,在訓(xùn)練模型時(shí),我們面臨一個(gè)問(wèn)題:我們不希望一本一本地去借書(shū)(效率太低),也不可能一次性把整個(gè)圖書(shū)館的書(shū)都搬過(guò)來(lái)(內(nèi)存會(huì)爆炸)。

      這時(shí),DataLoader 就登場(chǎng)了。它就像一個(gè)超級(jí)智能的物流團(tuán)隊(duì),負(fù)責(zé)高效地從圖書(shū)館那里(Dataset)搬運(yùn)書(shū)籍(數(shù)據(jù))給在辦公室里等著工作的你(模型)。

      def prep_dataloader(path, mode, batch_size, n_jobs=0, target_only=False):
          ''' Generates a dataset, then is put into a dataloader. '''
          dataset = COVID19Dataset(path, mode=mode, target_only=target_only)  # Construct dataset
          dataloader = DataLoader(
              dataset, batch_size,
              shuffle=(mode == 'train'), drop_last=False,
              num_workers=n_jobs, pin_memory=True)                            # Construct dataloader
          return dataloader
      

      這個(gè)函數(shù)是一個(gè)"包裝"函數(shù),它做了兩件事情:

      1. 創(chuàng)建Dataset對(duì)象:就是利用我們之前定義的COVID19Dataset類(lèi)創(chuàng)建了一個(gè)數(shù)據(jù)集實(shí)例

      2. 創(chuàng)建DataLoader對(duì)象dataloader = DataLoader(), 它接收上一步創(chuàng)建的dataset,并用一系列參數(shù)對(duì)它進(jìn)行配置,把它變成一個(gè)數(shù)據(jù)加載器

      DataLoader的核心參數(shù)

      datloader = DataLoader(
          dateset,            # 1. 數(shù)據(jù)集
          batch_size,         # 2. 批次大小
          shuffle = ...       # 3. 是否打亂
          drop_last = False,  # 4. 是否丟棄最后一個(gè)不完整的批次
          num_workers = ...   # 5. 使用多個(gè)子進(jìn)程加載數(shù)據(jù)
          pin_memory = True   # 6. 是否鎖頁(yè)內(nèi)存
      )
      
      • dataset

        • 它是什么:我們傳入的COVID19Dataset實(shí)例
        • 為什么需要DataLoader需要知道它的數(shù)據(jù)源頭在哪里,也就是要去哪個(gè)"圖書(shū)館"搬書(shū)
      • batch_size(批次大小)

        • 決定了每次打包多少條數(shù)據(jù)(詳見(jiàn)mini-batch
      • shuffle = (mode = 'train') 是否打亂

        • 如果為True,DataLoader會(huì)在每一輪(epoch)訓(xùn)練開(kāi)始前,都將數(shù)據(jù)的順序完全隨機(jī)打亂

      小結(jié)

      整個(gè)數(shù)據(jù)流:

      原始CSV文件 \(\rightarrow\) Daset類(lèi)(定義了如何讀取和處理單條數(shù)據(jù)) \(\rightarrow\) DataLoader(負(fù)責(zé)高效地、批量地、可選地打亂數(shù)據(jù),并將其打包好) \(\rightarrow\) 一個(gè)個(gè)批次(batch)的數(shù)據(jù)(最終送入模型訓(xùn)練)

      Step4. 深度神經(jīng)網(wǎng)絡(luò)(DNN)

      完整代碼

      class NeuralNet(nn.Module):
          ''' A simple fully-connected deep neural network '''
          def __init__(self, input_dim):
              super(NeuralNet, self).__init__()
      
              # Define your neural network here
              # TODO: How to modify this model to achieve better performance?
              self.net = nn.Sequential(
                  nn.Linear(input_dim, 64),
                  nn.ReLU(),
                  nn.Linear(64, 1)
              )
      
              # Mean squared error loss
              self.criterion = nn.MSELoss(reduction='mean')
      
          def forward(self, x):
              ''' Given input of size (batch_size x input_dim), compute output of the network '''
              return self.net(x).squeeze(1)
      
          def cal_loss(self, pred, target):
              ''' Calculate loss '''
              # TODO: you may implement L2 regularization here
              return self.criterion(pred, target)
      

      class NeuralNet(nn.Module) 相當(dāng)于樂(lè)高創(chuàng)意工坊

      class NeuralNet(nn.Module): 
      

      在PyTorch中,所有自定義的模型都必須繼承自torch.nn.Module這個(gè)類(lèi),它可以:

      • 參數(shù)跟蹤:它會(huì)自動(dòng)識(shí)別我們模型中所有需要學(xué)習(xí)的參數(shù),我們不需要手動(dòng)管理
      • 設(shè)備轉(zhuǎn)移:我們可以使用.to('cuda')這樣的命令,吧整個(gè)模型(包括所有參數(shù))搬到GPU上加速運(yùn)算
      • 模型保存與加載:提供了方便的.state_dict().load_state_dict()方法來(lái)保存和加載你訓(xùn)練好的模型
      • 模式切換: 可以用.train().eval()切換訓(xùn)練模式和評(píng)估模式

      __init__(self, input_dim) 準(zhǔn)備樂(lè)高積木
      這個(gè)方法是模型的構(gòu)造函數(shù),負(fù)責(zé)定義初始化我們的神經(jīng)網(wǎng)絡(luò)擁有的所有"積木塊"(

      def __init__(self, input_dim):
          super(NeuralNet, self).__init__() #必須的開(kāi)場(chǎng)白
      
          #--- 把積木搭好 ---#
          self.net = nn.Sqeuential(
              nn.Linear(input_dim, 64),
              nn.ReLU(),
              nn.Linear(64, 1)
          )
      
          # --- 準(zhǔn)備好評(píng)分標(biāo)準(zhǔn) ---
          self.criterion = nn.MSELoss(reduction='mean')
      
      1. super(NeurlaNet, self).__init__()
        這是一句必須在最開(kāi)始調(diào)用的代碼,它會(huì)運(yùn)行父類(lèi)nn.Module的初始化邏輯,確保我們的模型工坊能正常運(yùn)作

      2. self.net = nn.Sequential()
        nn.Sequential是一個(gè)非常有用的“容器”或者“流水線管道”,我們可以吧一系列樂(lè)高積木塊()按順序放進(jìn)去,當(dāng)數(shù)據(jù)從管道一頭進(jìn)去時(shí),會(huì)自動(dòng)地、依次地通過(guò)所有積木塊,最后從另一頭出來(lái),這讓我們的forward方法可以寫(xiě)得很簡(jiǎn)潔

      3. 流水線里的“積木塊”

        • nn.Linear(input_dim, 64)全連接層,這個(gè)應(yīng)該都知道了,畢竟接觸到第一個(gè)神經(jīng)網(wǎng)絡(luò)就是這個(gè),它對(duì)輸入數(shù)據(jù)進(jìn)行一次線性變換(y = Wx + b),可以理解為將輸入的特征進(jìn)行加權(quán)、混合,然后提煉出新的特征
          • nn.Linear內(nèi)部已經(jīng)包含了需要學(xué)習(xí)的權(quán)重矩陣W和偏置向量b,并且PyTorch會(huì)自動(dòng)對(duì)它們進(jìn)行隨機(jī)初始化
          • input_dim:輸入特征數(shù)量。比如,我們的數(shù)據(jù)有93個(gè)特征,這里就是93
          • 64:輸入神經(jīng)元的數(shù)量,這代表我們希望這個(gè)層提煉出64個(gè)新的特征,這是一個(gè)可以自己調(diào)整的超參數(shù)
      • nn.ReLU():激活函數(shù)
        • 給網(wǎng)絡(luò)引入非線性,如果沒(méi)有非線性層,那么無(wú)論我們堆疊多少個(gè)nn.Linear層,整個(gè)網(wǎng)絡(luò)本質(zhì)上還是線性的,理論上一個(gè)線性層就替代了,(那么堆疊多層純純小丑),學(xué)習(xí)能量非常有限,非線性激活函數(shù)能增加模型復(fù)雜度,提高模型的彈性
        • nn.Linear(64, 1):輸出層
          • 64: 它的輸入數(shù)量必須和上一層的輸出數(shù)量保持一致,這樣才能銜接起來(lái)
          • 1:它的輸出數(shù)量是1,因?yàn)槲覀兊娜蝿?wù)是預(yù)測(cè)一個(gè)單獨(dú)的數(shù)值(確診人數(shù)),所以最終只需要一個(gè)輸出結(jié)果
      1. self.criterion = nn.MSELoss()
        • 模型的“評(píng)分標(biāo)準(zhǔn)”,也就是損失函數(shù),nn.MSELoss()是PyTorch內(nèi)置的均方誤差損失函數(shù)

      forward(self, x)前向傳播 說(shuō)明書(shū)

      這個(gè)方法定義了數(shù)據(jù)如何流過(guò)我們?cè)?code>__init__中準(zhǔn)備好的積木塊

      def forward(self, x):
          # 數(shù)據(jù) x 直接通過(guò) self.net 這條搭建好的流水線
          return self.net(x).squeeze(1)
      
      • x: 代表一批輸入的數(shù)據(jù)(一個(gè)Tensor)
      • self.net(x): 因?yàn)槲覀兪褂昧?code>nn.Sequential,所以這里的代碼異常簡(jiǎn)潔,我們直接吧數(shù)據(jù)x喂給self.net這個(gè)管道,它就會(huì)自動(dòng)地按照Linear -> ReLU -> Linear的順序進(jìn)行計(jì)算,并返回最終結(jié)果
      • .squeeze(1): 這是一個(gè)形狀調(diào)整操作。self.net輸出的形狀是[批次大小, 1],而我們的真實(shí)標(biāo)簽是[批次大小].squeeze(1)會(huì)擠掉那個(gè)多余的維度1,讓預(yù)測(cè)和標(biāo)簽的形狀匹配,方便后續(xù)計(jì)算損失

      小結(jié)

      \(NeuralNet\)如何工作?

      1. 創(chuàng)建模型: 當(dāng)我們寫(xiě) model = NeuralNet(input_dim=93) 時(shí),__init__ 方法被調(diào)用,模型的所有“積木塊”(層)都被創(chuàng)建并準(zhǔn)備好。

      2. 進(jìn)行預(yù)測(cè): 在訓(xùn)練循環(huán)中,當(dāng)我們寫(xiě) prediction = model(data) 時(shí),PyTorch 會(huì)自動(dòng)調(diào)用 forward(data) 方法。數(shù)據(jù)會(huì)按照我們定義的路徑流過(guò)整個(gè)網(wǎng)絡(luò),最終得到預(yù)測(cè)結(jié)果。

      3. 計(jì)算誤差: 接著,我們調(diào)用 loss = model.cal_loss(prediction, target) 來(lái)計(jì)算預(yù)測(cè)的好壞。


      Step5. Train/Dev/Test

      5.1 Train

      def train(tr_set, dv_set, model, config, device):
          ''' DNN training '''
      
          n_epochs = config['n_epochs']  # Maximum number of epochs
      
          # Setup optimizer
          optimizer = getattr(torch.optim, config['optimizer'])(
              model.parameters(), **config['optim_hparas'])
      
          min_mse = 1000.
          loss_record = {'train': [], 'dev': []}      # for recording training loss
          early_stop_cnt = 0
          epoch = 0
          while epoch < n_epochs:
              model.train()                           # set model to training mode
              for x, y in tr_set:                     # iterate through the dataloader
                  optimizer.zero_grad()               # set gradient to zero
                  x, y = x.to(device), y.to(device)   # move data to device (cpu/cuda)
                  pred = model(x)                     # forward pass (compute output)
                  mse_loss = model.cal_loss(pred, y)  # compute loss
                  mse_loss.backward()                 # compute gradient (backpropagation)
                  optimizer.step()                    # update model with optimizer
                  loss_record['train'].append(mse_loss.detach().cpu().item())
      
              # After each epoch, test your model on the validation (development) set.
              dev_mse = dev(dv_set, model, device)
              if dev_mse < min_mse:
                  # Save model if your model improved
                  min_mse = dev_mse
                  print('Saving model (epoch = {:4d}, loss = {:.4f})'
                      .format(epoch + 1, min_mse))
                  torch.save(model.state_dict(), config['save_path'])  # Save model to specified path
                  early_stop_cnt = 0
              else:
                  early_stop_cnt += 1
      
              epoch += 1
              loss_record['dev'].append(dev_mse)
              if early_stop_cnt > config['early_stop']:
                  # Stop training if your model stops improving for "config['early_stop']" epochs.
                  break
      
          print('Finished training after {} epochs'.format(epoch))
          return min_mse, loss_record
      

      1. 訓(xùn)練前的準(zhǔn)備工作

      n_epochs = config['n_epochs'] # 學(xué)習(xí)輪數(shù)
      # --- 定義優(yōu)化器 ---
      optimizer = getattr(torch.optim, config['optimizer'])(
          model.parameters(), **config['optim_hparas'])
      # --- 狀態(tài)跟蹤變量 ---
      min_mse = 1000.
      loss_record = {'train': [], 'dev': []}
      early_stop_cnt = 0
      epoch = 0
      
      • n_epoches:從配置中讀取“訓(xùn)練輪數(shù)”,也就是我們打算讓模型吧整個(gè)訓(xùn)練數(shù)據(jù)集重復(fù)學(xué)習(xí)多少遍
      • optimizer:優(yōu)化器
        • 簡(jiǎn)單說(shuō),就是一種優(yōu)化梯度下降的方法,由于有的損失函數(shù)的梯度不是很容易降下來(lái),我們可以利用各種優(yōu)化算法。比如 SGD (隨機(jī)梯度下降), Adam 等。這里的代碼 getattr(torch.optim, config['optimizer']) 是一種非常靈活的寫(xiě)法,可以根據(jù)我們?cè)?code>config字典里設(shè)置的字符串(比如 'SGD''Adam')來(lái)自動(dòng)選擇并創(chuàng)建對(duì)應(yīng)的優(yōu)化器
        • model.parameters(): 這是nn.Module帶來(lái)的便利之一,整個(gè)方法會(huì)自動(dòng)返回模型中所有需要學(xué)習(xí)的參數(shù),也就是所有nn.Linear層的權(quán)重和偏置,我們把這些參數(shù)交給優(yōu)化器,優(yōu)化器就知道去更新誰(shuí)了
      • min_mse, loss_record, early_step_cnt: 用于記錄和監(jiān)控訓(xùn)練過(guò)程的變量
        • min_mse: 記錄驗(yàn)證集上出現(xiàn)過(guò)的最小均方誤差,用來(lái)判斷是否要保存模型
        • loss_record: 記錄每一輪訓(xùn)練和驗(yàn)證的損失值,方便后續(xù)畫(huà)出“學(xué)習(xí)曲線”
        • early_stop_cnt: 用于提前停止,用來(lái)防止模型在沒(méi)有進(jìn)步的情況下繼續(xù)浪費(fèi)時(shí)間訓(xùn)練

      2. 主學(xué)習(xí)循環(huán)

      while epoch < n_epochs:
          # ... 一輪學(xué)習(xí)的完整過(guò)程 ...
          epoch += 1
      
      • Epoch : 所有batch跑一遍,也就是把整個(gè)訓(xùn)練數(shù)據(jù)集從頭到尾學(xué)習(xí)一遍,也就是while循環(huán)執(zhí)行一次,就是一個(gè)Epoch(輪)

      3. 批次學(xué)習(xí)循環(huán)

      在每一個(gè)Epoch學(xué)習(xí)中,模型會(huì)一小批一小批地(mini-batch)看數(shù)據(jù)

      model.train() # 1. 切換到訓(xùn)練模式
          for x, y in tr_set: # tr_set 就是我們的 DataLoader
              # --- 核心訓(xùn)練五步法 ---
              optimizer.zero_grad()               # 2. 梯度歸零
              x, y = x.to(device), y.to(device)   # 3. 數(shù)據(jù)上膛(放到GPU)
              pred = model(x)                     # 4. 前向傳播(做預(yù)測(cè))
              mse_loss = model.cal_loss(pred, y)  # 5. 計(jì)算損失
              mse_loss.backward()                 # 6. 反向傳播(計(jì)算梯度)
              optimizer.step()                    # 7. 更新權(quán)重
              
              loss_record['train'].append(mse_loss.detach().cpu().item())
      
      • model_train(): 這是nn.Module的一個(gè)重要方法。這會(huì)啟用一些只有在訓(xùn)練時(shí)才使用的功能(比如Dropout),與之對(duì)應(yīng)的是model.eval(),在驗(yàn)證集和測(cè)試時(shí)使用
      • for x, y in tr_set: tr_set 是我們的 DataLoader。這個(gè)循環(huán)會(huì)不斷地從 DataLoader 中取出打包好的小批次數(shù)據(jù),x 是特征,y 是標(biāo)簽。

      接下來(lái)的幾步是 PyTorch 中最經(jīng)典、最核心的訓(xùn)練流程, 幾乎所有的訓(xùn)練代碼都遵循這個(gè)模式,甚至可以當(dāng)成模板


      • optimizer.zero_grad() 梯度歸零
        • PyTorch會(huì)默認(rèn)累積梯度,因此,在計(jì)算新一批數(shù)據(jù)的梯度之前,必須手動(dòng)清空上一批的梯度。否則,梯度會(huì)越加越大,導(dǎo)致錯(cuò)誤的更新
      • x, y = x.to(device), y.to(device):
        • 將這一批數(shù)據(jù)和標(biāo)簽都移動(dòng)到之前設(shè)定的設(shè)備(GPU或者CPU)上
      • pred = model(x)
        • 把數(shù)據(jù) x 喂給模型,模型會(huì)調(diào)用自己的 forward 方法,得出一個(gè)預(yù)測(cè)結(jié)果 pred
      • mes_loss = model.cal_loss(pred, y) 計(jì)算損失
        • 調(diào)用我們之前定義的 cal_loss 方法,用損失函數(shù)(MSE)比較預(yù)測(cè)值 pred 和真實(shí)值 y,得到一個(gè)代表“差距”的數(shù)值 mse_loss
      • mes_loss.backward() 反向傳播
        • autograd:這是PyTorch的“自動(dòng)求導(dǎo)引擎”,調(diào)用.backward()后,PyTorch會(huì)自動(dòng)計(jì)算出mes_loss相對(duì)于模型中每一個(gè)需要學(xué)習(xí)的參數(shù)的梯度(偏導(dǎo)數(shù)),這個(gè)梯度指明了參數(shù)應(yīng)該朝哪個(gè)方向調(diào)整才能讓損失變小
      • optimizer.step() 更新權(quán)重
        • 優(yōu)化器optimizer會(huì)根據(jù)上一步計(jì)算出的梯度,使用它自己的更新規(guī)則(比如 SGDAdam算法)來(lái)微調(diào)模型中所有的參數(shù) (model.parameters()。這一步是模型真正在“學(xué)習(xí)”和“成長(zhǎng)”的時(shí)刻。


      4. 驗(yàn)證和保存

      在一輪(Epoch)的所有批次都學(xué)習(xí)完后,我們需要對(duì)模型進(jìn)行一次驗(yàn)證,看看它學(xué)得怎么樣。

      dev_mse = dev(dv_set, model, device) # 在驗(yàn)證集上考試
          if dev_mse < min_mse:
              # 如果這次考試成績(jī)是歷史最好成績(jī)...
              min_mse = dev_mse
              print('Saving model ...')
              torch.save(model.state_dict(), config['save_path']) # 保存模型
              early_stop_cnt = 0 # 重置早停計(jì)數(shù)器
          else:
              early_stop_cnt += 1 # 否則,沒(méi)進(jìn)步的次數(shù)+1
      
      • dev(dv_set, model, device): 調(diào)用 dev 函數(shù)在驗(yàn)證集上進(jìn)行評(píng)估,得到一個(gè)均方誤差 dev_mse

      • if dev_mse < min_mse: 如果這次的驗(yàn)證誤差比歷史最低誤差 min_mse 還要低,說(shuō)明模型取得了進(jìn)步。

      • torch.save(model.state_dict(), ...): 這是保存模型的標(biāo)準(zhǔn)方法。model.state_dict() 會(huì)返回一個(gè)包含模型所有學(xué)習(xí)到的參數(shù)(權(quán)重和偏置)的字典。torch.save 將這個(gè)字典保存到文件中,這樣我們以后就可以隨時(shí)加載這個(gè)表現(xiàn)最好的模型了。


      5. 早停機(jī)制

      if early_stop_cnt > config['early_stop']:
              # 如果連續(xù)很多輪考試成績(jī)都沒(méi)進(jìn)步...
              break # ...就提前結(jié)束訓(xùn)練
      

      這個(gè)機(jī)制可以防止在模型性能不再提升時(shí)繼續(xù)浪費(fèi)時(shí)間訓(xùn)練,同時(shí)也能有效避免過(guò)擬合。


      小結(jié)

      train 函數(shù)模擬了學(xué)習(xí)過(guò)程:它讓模型在一輪輪(epoch)的學(xué)習(xí)中,一小批一小批(batch)地看數(shù)據(jù),并通過(guò) 前向傳播 -> 計(jì)算損失 -> 反向傳播 -> 更新權(quán)重 的核心流程來(lái)不斷優(yōu)化自己。同時(shí),它還通過(guò)驗(yàn)證集來(lái)監(jiān)控學(xué)習(xí)效果,只保存最好的模型,并在必要時(shí)提前終止訓(xùn)練。

      5.2 Validation

      def dev(dv_set, model, device):
          model.eval()                                # 1. 切換到評(píng)估模式
          total_loss = 0
          for x, y in dv_set:                         # 2. 遍歷驗(yàn)證數(shù)據(jù)
              x, y = x.to(device), y.to(device)
              with torch.no_grad():                   # 3. 關(guān)閉梯度計(jì)算
                  pred = model(x)                     # 4. 做預(yù)測(cè)
                  mse_loss = model.cal_loss(pred, y)  # 5. 計(jì)算損失
              total_loss += mse_loss.detach().cpu().item() * len(x)
          total_loss = total_loss / len(dv_set.dataset)
          return total_loss
      

      def dev(dv_set, model, device) 模擬考試

      在每一個(gè)epoch結(jié)束后,用驗(yàn)證集dv_set來(lái)評(píng)估一下模型當(dāng)前的表現(xiàn),得到一個(gè)客觀的分?jǐn)?shù)

      5.3 Testing

      def test(tt_set, model, device):
          model.eval()                                # 同樣切換到評(píng)估模式
          preds = []
          for x in tt_set:                            # 1. 遍歷測(cè)試數(shù)據(jù) (注意這里只有 x)
              x = x.to(device)
              with torch.no_grad():                   # 同樣關(guān)閉梯度計(jì)算
                  pred = model(x)                     # 2. 做預(yù)測(cè)
                  preds.append(pred.detach().cpu())   # 3. 收集預(yù)測(cè)結(jié)果
          preds = torch.cat(preds, dim=0).numpy()     # 4. 拼接并轉(zhuǎn)換為 NumPy 數(shù)組
          return preds
      

      def test(tt_set, model, device) 高考

      這個(gè)函數(shù)在所有訓(xùn)練都完成之后才被調(diào)用。它使用我們保存下來(lái)的表現(xiàn)最好的模型,對(duì)從未見(jiàn)過(guò)的測(cè)試集 tt_set 進(jìn)行預(yù)測(cè),并生成最終的結(jié)果文件。

      注意
      流程與 dev 的異同

      • 相同點(diǎn): 同樣需要 model.eval()with torch.no_grad(),因?yàn)檫@也是一個(gè)評(píng)估過(guò)程。

      • 不同點(diǎn):

        • 輸入: 遍歷測(cè)試集 tt_set 時(shí),DataLoader 只返回 x,因?yàn)闇y(cè)試集沒(méi)有提供標(biāo)簽 y

        • 目的: test 函數(shù)的目的不是計(jì)算損失,而是收集所有預(yù)測(cè)結(jié)果 pred

        • 輸出: dev 返回一個(gè)數(shù)字(損失值),而 test 返回一個(gè)包含所有預(yù)測(cè)值的數(shù)組。

      1. preds.append(pred.detach().cpu())
        在循環(huán)中,我們將每一批的預(yù)測(cè)結(jié)果(一個(gè)Tensor)從GPU上分離并移到CPU,然后存入一個(gè)Python列表preds

      2. preds = torch.cat(preds, dim = 0).numpy()
        這個(gè)是循環(huán)結(jié)束后的一個(gè)重要處理步驟

        • torch.cat(preds, dim=0): preds 現(xiàn)在是一個(gè) Tensor 列表 [tensor_batch1, tensor_batch2, ...]torch.cat 函數(shù)的作用是將這個(gè)列表中的所有 Tensor 拼接成一個(gè)大的、完整的 Tensordim=0 指定了沿著第0個(gè)維度(也就是行)進(jìn)行拼接。

        • .numpy(): 將最終的 PyTorch Tensor 轉(zhuǎn)換成 NumPy 數(shù)組。這樣做是為了方便后續(xù)的處理,比如保存成 CSV 文件,或者使用其他非 PyTorch 的庫(kù)(如 Scikit-learn)進(jìn)行分析

      小結(jié)

      階段 訓(xùn)練 (Training) 驗(yàn)證 (Validation) 測(cè)試 (Testing)
      目的 學(xué)習(xí)知識(shí),更新權(quán)重 檢查學(xué)習(xí)效果,調(diào)整策略 最終評(píng)估模型性能
      模式 model.train() model.eval() model.eval()
      梯度 需要 (.backward()) 不需要 (torch.no_grad) 不需要 (torch.no_grad)
      數(shù)據(jù) 特征 x 和標(biāo)簽 y 特征 x 和標(biāo)簽 y 只有特征 x
      輸出 無(wú)(只更新模型內(nèi)部參數(shù)) 損失值(一個(gè)數(shù)字) 所有預(yù)測(cè)結(jié)果(一個(gè)數(shù)組)


      考慮優(yōu)化

      好了,這樣整個(gè)流程就基本結(jié)束了,我們就可以考慮去優(yōu)化我們的神經(jīng)網(wǎng)絡(luò)了,這里作業(yè)給的源碼其實(shí)是可以直接跑的,但是因?yàn)樯窠?jīng)網(wǎng)絡(luò)設(shè)計(jì)得比較簡(jiǎn)單所以最后的結(jié)果會(huì)比較差

      說(shuō)明模型的準(zhǔn)確率不夠,也就是說(shuō),模型的學(xué)習(xí)能力比較弱,可能有這幾點(diǎn)原因

      • 數(shù)據(jù)量不夠大
      • 特征太少
      • 結(jié)構(gòu)太簡(jiǎn)單
      • 梯度降不下去,損失函數(shù)的值一直很大

      作業(yè)中的數(shù)據(jù)集是給定的,我們沒(méi)法改了
      我們可以從剩下的方面考慮

      --------------------------------------------Waiting for update----------------------------------------------------------

      posted @ 2025-10-12 17:55  栗悟飯與龜功気波  閱讀(7)  評(píng)論(0)    收藏  舉報(bào)
      主站蜘蛛池模板: 亚洲中文无码av在线| 国产成人精品亚洲高清在线| 韩国无码AV片午夜福利| 最近2019中文字幕大全第二页| av鲁丝一区鲁丝二区鲁丝三区 | 国产亚洲国产精品二区| 免费无码毛片一区二三区| jlzz大jlzz大全免费| 日韩无矿砖一线二线卡乱| 国产成人无码A区在线观| gogogo高清在线观看视频中文| 亚洲精品成人久久av| 一级做a爰片在线播放| www插插插无码免费视频网站| 狠狠躁夜夜躁人人爽天天5| 国产精品一码二码三码四码| 无码日韩做暖暖大全免费不卡| 国产精品亚洲二区在线看| 熟女系列丰满熟妇AV| 青草青草久热精品视频在线观看 | 亚洲一区二区三区人妻天堂| 人妻少妇精品中文字幕| 亚洲色av天天天天天天| 亚洲一区二区无码影院| 高清偷拍一区二区三区| 亚洲国产精品自产在线播放| 蜜臀av入口一区二区三区| 欧美韩中文精品有码视频在线| 连江县| 国产极品粉嫩学生一线天| 麻豆国产成人AV在线播放| 久章草在线精品视频免费观看| 视频一区二区不中文字幕| 色吊丝二区三区中文字幕| 经典国产乱子伦精品视频| 精品久久久久久国产| 国产中文字幕精品视频| 国产精品夜夜春夜夜爽久久小| 国产精品自拍视频第一页| 国产精品亚洲第一区在线| 99国产欧美另类久久久精品|