hw1例题-李宏毅老师课后作业
寫(xiě)在前面
跟著李宏毅老師從頭開(kāi)始學(xué)習(xí)深度學(xué)習(xí),因此這里用CSDN記錄深度學(xué)習(xí)的課后例題,需要注意的是以下代碼來(lái)源于網(wǎng)上,應(yīng)該是李老師的代碼,版權(quán)不屬于我,寫(xiě)這篇博客主要為了鞏固自己關(guān)于機(jī)器(深度)學(xué)習(xí)的知識(shí)。
問(wèn)題描述
hw1是Kaggle原題(https://www.kaggle.com/competitions/ml2021spring-hw1/overview),簡(jiǎn)單來(lái)說(shuō)是預(yù)測(cè)Covid19的陽(yáng)性人數(shù)。提供的文件有covid.train.csv,covid.test.csv 和sampleSubmission.csv。顯然任務(wù)是根據(jù)訓(xùn)練集的數(shù)據(jù)訓(xùn)練一個(gè)模型,并實(shí)現(xiàn)測(cè)試集的預(yù)測(cè)。
代碼部分
導(dǎo)入庫(kù)
import pandas as pd from torch import nn import numpy as np from torch.utils.data import Dataset, DataLoader, random_split from torch.utils.tensorboard import SummaryWriter import math from tqdm import tqdm #tqdm庫(kù)用于生成訓(xùn)練時(shí)的進(jìn)度條展示 import os import csv這里的庫(kù)只有tqdm庫(kù)不太了解,注釋說(shuō)是用于進(jìn)度條展示,所以應(yīng)該是類(lèi)似于用戶(hù)友好的圖形處理界面。
建立數(shù)據(jù)集函數(shù)
class COVID19Dataset(Dataset):def __init__(self,x,y=None):if y is None:self.y=yelse:self.y=torch.FloatTensor(y)self.x=torch.FloatTensor(x)def __getitem__(self,idx):if self.y is None:return self.x[idx]else:return self.x[idx],self.y[idx]def __len__(self):return len(self.x)上述函數(shù)建立了covid19dataset的類(lèi),繼承于torch庫(kù)里的dataset類(lèi)并覆寫(xiě)了其中的內(nèi)嵌函數(shù)__getitem,添加了函數(shù)__len__。(在此之前我一般做法都是直接讀取csv文件獲取數(shù)據(jù)集,這里學(xué)習(xí)到了)
特征提取
def select_feature(train_data,valid_data,test_data,select_all=True):y_train,y_valid=train_data[:,-1],valid_data[:,-1]raw_x_train,raw_x_valid,raw_x_test=train_data[:,:-1],valid_data[:,:-1],test_dataif select_all:feature_idx=list(range(raw_x_train.shape[1]))else:feature_idx=[0,1,2,3,4]#print(raw_x_train)return raw_x_train[:,feature_idx],raw_x_valid[:,feature_idx],raw_x_test[:,feature_idx],y_train,y_valid感覺(jué)這里寫(xiě)的簡(jiǎn)單了很多,對(duì)于特征提取應(yīng)該可以更精細(xì)一點(diǎn)。雖然這個(gè)代碼塊可以實(shí)現(xiàn)特征選擇,但是選擇哪些特征并沒(méi)有給出,應(yīng)該對(duì)這些特征做一個(gè)比較細(xì)致的分析例如相關(guān)系數(shù)分析。
設(shè)置隨機(jī)種子
def same_seed(seed):torch.backends.cudnn.deterministic=Truetorch.backends.cudnn.benchmark=Falsenp.random.seed(seed)torch.manual_seed(seed)if torch.cuda.is_avaliable():torch.cuda.manual_seed_all(seed)隨機(jī)劃分訓(xùn)練集和驗(yàn)證集
def train_valid_split(dataset,ratio,seed):valid_set_size=int(ratio*len(dataset))train_set_size=len(dataset)-valid_set_sizetrain_set,valid_set=random_split(dataset,[train_set_size,valid_set_size],generator=torch.Generator().manual_seed(seed))return np.array(train_set.dataset.iloc[train_set.indices,:]), np.array(valid_set.dataset.iloc[valid_set.indices,:])這里我寫(xiě)的與原始代碼不同,主要是最后的返回值,按常理來(lái)看這里應(yīng)該是返回訓(xùn)練集和驗(yàn)證集的矩陣形式,但按照原代碼的結(jié)果返回的卻是兩個(gè)torch.utils.data.dataset.Subset對(duì)象。
定義模型
class My_Model(nn.Module):def __init__(self,input_dim):super(My_Model,self).__init__()self.layers=nn.Sequential(nn.Linear(input_dim,16),nn.ReLU(),nn.Linear(16,8),nn.ReLU(),nn.Linear(8,1))def forward(self,x):x=self.layers(x)x=x.squeeze(1)return x顯然這部分代碼是定義了一個(gè)神經(jīng)網(wǎng)絡(luò),有2個(gè)隱藏層,神經(jīng)元個(gè)數(shù)分別為16和8,ReLU為激活函數(shù)。
定義訓(xùn)練函數(shù)
def trainer(train_loader,valid_loader,model,config,device):criterion=nn.MSELoss(reduction='mean')optimizer=torch.optim.SGD(model.parameters(),lr=config['learning_rate'],momentum=0.9)writer=SummaryWriter()if not os.path.isdir('./models'):os.mkdir('./models')n_epochs,best_loss,step,early_stop_count=config['n_epochs'],math.inf,0,0for epoch in range(n_epochs):model.train()loss_record=[]train_pbar=tqdm(train_loader,position=0,leave=True)for x,y in train_pbar:optimizer.zero_grad()x,y=x.to(device),y.to(device)pred=model(x)loss=criterion(pred,y)loss.backward()optimizer.step()step+=1loss_record.append(loss.detach().item())train_pbar.set_description(f'Epoch [{epoch+1}/{n_epochs}]')train_pbar.set_postfix({'loss':loss.detach().item()})mean_train_loss=sum(loss_record)/len(loss_record)writer.add_scalar('Loss/train', mean_train_loss, step) #tensoboard畫(huà)出loss曲線model.eval()loss_record=[]for x,y in valid_loader:x,y=x.to(device),y.to(device)with torch.no_grad():pred=model(x)loss=criterion(pred,y)loss_record.append(loss.item())mean_valid_loss=sum(loss_record)/len(loss_record)print(f'Epoch [{epoch+1}/{n_epochs}]: Train loss: {mean_train_loss:.4f}, Valid loss: {mean_valid_loss:.4f}')writer.add_scalar('Loss/valid', mean_valid_loss, step)if mean_valid_loss<best_loss:best_loss=mean_valid_losstorch.save(model.state_dict(),config['save_path'])print(f'Saving model with loss {best_loss:.3f}...')early_stop_count=0else:early_stop_count+=1if early_stop_count>=config['early_stop_count']:print('\nModel is mot improving, so we halt the training session.')return該部分十分重要,可以說(shuō)是核心代碼了,所以有必要進(jìn)行詳細(xì)的說(shuō)明。這部分代碼即是為了完成模型的訓(xùn)練。函數(shù)的參數(shù)有訓(xùn)練集、驗(yàn)證集、自定義配置和設(shè)備(cpu or gpu)。首先定義了損失函數(shù)和優(yōu)化器,算是常見(jiàn)的操作了,SummaryWriter是我第一次見(jiàn)到,與tensorboard相關(guān),后續(xù)會(huì)仔細(xì)再看看。之后定義了存儲(chǔ)位置和epoch,best_loss以及early_stop_count。early_stop_count在我用XGBoost時(shí)經(jīng)常遇到,主要是為了停止損失基本不下降模型的訓(xùn)練。下面正式進(jìn)入epoch,在每一個(gè)epoch中:首先model.train(),表明模型開(kāi)啟訓(xùn)練模式,打開(kāi)batch normalization和drop out,與之對(duì)應(yīng)的是model.eval(),即將對(duì)應(yīng)的設(shè)置關(guān)閉,在驗(yàn)證集上使用。train_pbar用到了tqdm類(lèi),可視化了遍歷訓(xùn)練集的進(jìn)度,position設(shè)置了打印進(jìn)度條的位置,leave是表示執(zhí)行完成后是否保留進(jìn)度條。隨后開(kāi)始遍歷訓(xùn)練集,將輸入與輸出分別轉(zhuǎn)移到相應(yīng)的device上,之后定義損失函數(shù),同時(shí)將得到的損失記錄下來(lái),遍歷完之后,計(jì)算平均損失。之后進(jìn)入模型驗(yàn)證,這里就要關(guān)閉模型的訓(xùn)練模式,因?yàn)槟P万?yàn)證是為了觀察模型在訓(xùn)練之后在驗(yàn)證集的表現(xiàn),所以這里torch.no_grad()表明無(wú)需對(duì)模型計(jì)算梯度,只需要計(jì)算損失即可。最后比較驗(yàn)證集上的平均損失與最低損失,如果小于則更新最低損失為當(dāng)前平均損失并將early_stop_count設(shè)為0,否則early_stop_count+=1。
開(kāi)始:
框架已經(jīng)搭好,后面就是正式開(kāi)始訓(xùn)練模型,這部分內(nèi)容比較簡(jiǎn)單易懂,就不多贅述。
讀取并劃分?jǐn)?shù)據(jù)
device = 'cuda' if torch.cuda.is_available() else 'cpu' config={'seed':0,'select_all':True,'valid_ratio':0.2,'n_epochs':3000,'batch_size':256,'learning_rate':1e-5,'early_stop_count':400,'save_path': './models/model.ckpt' }train_data=pd.read_csv(r'D:\Work Studio\Python 3.8.6\My secret base\hw\hw1\ml2021spring-hw1\covid.train.csv').iloc[:,1:] test_data=np.array(pd.read_csv(r'D:\Work Studio\Python 3.8.6\My secret base\hw\hw1\ml2021spring-hw1\covid.test.csv').iloc[:,1:])train_data, valid_data = train_valid_split(train_data, config['valid_ratio'], config['seed']) x_train, x_valid, x_test, y_train, y_valid = select_feature(train_data, valid_data, test_data, config['select_all']) train_dataset, valid_dataset, test_dataset = COVID19Dataset(x_train, y_train), \COVID19Dataset(x_valid, y_valid), \COVID19Dataset(x_test) train_loader = DataLoader(train_dataset, batch_size=config['batch_size'], shuffle=True, pin_memory=True) #pin_memory默認(rèn)false,打開(kāi)后更快 valid_loader = DataLoader(valid_dataset, batch_size=config['batch_size'], shuffle=True, pin_memory=True) test_loader = DataLoader(test_dataset, batch_size=config['batch_size'], shuffle=False, pin_memory=True)訓(xùn)練模型
model = My_Model(input_dim=x_train.shape[1]).to(device) # put your model and data on the same computation device. trainer(train_loader, valid_loader, model, config, device)保存模型并預(yù)測(cè)數(shù)據(jù)
def save_pred(preds, file):''' Save predictions to specified file '''with open(file, 'w') as fp:writer = csv.writer(fp)writer.writerow(['id', 'tested_positive'])for i, p in enumerate(preds):writer.writerow([i, p])model = My_Model(input_dim=x_train.shape[1]).to(device) model.load_state_dict(torch.load(config['save_path'])) preds = predict(test_loader, model, device) save_pred(preds, 'pred.csv')總結(jié)
以上即是hw1例題的基本內(nèi)容,經(jīng)過(guò)這一次的學(xué)習(xí)和探討,我對(duì)機(jī)器學(xué)習(xí)框架的建立有了更深刻的了解。后續(xù)如果有新的感悟和思考,會(huì)繼續(xù)更新補(bǔ)充,也歡迎大家批評(píng)指正。
總結(jié)
以上是生活随笔為你收集整理的hw1例题-李宏毅老师课后作业的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: BZOJ3668[NOI2014] 起床
- 下一篇: 统筹方法 -- 华罗庚