日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

NNDL 实验三 线性回归

發布時間:2024/8/1 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 NNDL 实验三 线性回归 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • NNDL 實驗三 線性回歸
    • 2.2 線性回歸
      • 2.2.1 數據集構建
      • 2.2.2 模型構建
      • 2.2.3 損失函數
      • 2.2.4 模型優化
      • 2.2.5 模型訓練
      • 2.2.6 模型評估
      • 2.2.7 樣本數量 & 正則化系數
    • 2.3 多項式回歸
      • 2.3.1 數據集構建
      • 2.3.2 模型構建
      • 2.3.3 模型訓練
      • 2.3.4 模型評估
    • 2.4 Runner類介紹
    • 2.5 基于線性回歸的波士頓房價預測
      • 2.5.1 數據處理
        • 2.5.1.1 數據清洗
        • 2.5.1.2 數據集劃分
        • 2.5.1.3特征工程
      • 2.5.2 模型構建
      • 2.5.3 完善Runner類
      • 2.5.4 模型訓練
      • 2.5.5 模型測試
      • 2.5.6 模型預測

NNDL 實驗三 線性回歸

使用pytorch實現

2.2 線性回歸

2.2.1 數據集構建

構造一個小的回歸數據集:
生成 150 個帶噪音的樣本,其中 100 個訓練樣本,50 個測試樣本,并打印出訓練數據的可視化分布。
代碼:

# 真實函數的參數缺省值為 w=1.2,b=0.5 def linear_func(x,w=1.2,b=0.5):y = w*x + breturn yimport torchdef create_toy_data(func, interval, sample_num, noise = 0.0, add_outlier = False, outlier_ratio = 0.001):# 均勻采樣# 使用torch.rand在生成sample_num個隨機數X = torch.rand(size = [sample_num]) * (interval[1]-interval[0]) + interval[0]y = func(X)# 生成高斯分布的標簽噪聲# 使用torch.normal生成0均值,noise標準差的數據epsilon = torch.normal(0,noise,y.shape)y = y + epsilonif add_outlier: # 生成額外的異常點outlier_num = int(len(y)*outlier_ratio)if outlier_num != 0:# 使用torch.randint生成服從均勻分布的、范圍在[0, len(y))的隨機Tensoroutlier_idx = torch.randint(len(y),size = [outlier_num])y[outlier_idx] = y[outlier_idx] * 5return X, yfrom matplotlib import pyplot as plt # matplotlib 是 Python 的繪圖庫 func = linear_func interval = (-10,10) train_num = 100 # 訓練樣本數目 test_num = 50 # 測試樣本數目 noise = 2 X_train, y_train = create_toy_data(func=func, interval=interval, sample_num=train_num, noise = noise, add_outlier = False) X_test, y_test = create_toy_data(func=func, interval=interval, sample_num=test_num, noise = noise, add_outlier = False)X_train_large, y_train_large = create_toy_data(func=func, interval=interval, sample_num=5000, noise = noise, add_outlier = False)# torch.linspace返回一個Tensor,Tensor的值為在區間start和stop上均勻間隔的num個值,輸出Tensor的長度為num X_underlying = torch.linspace(interval[0],interval[1],train_num) y_underlying = linear_func(X_underlying)# 繪制數據 plt.scatter(X_train, y_train, marker='*', facecolor="none", edgecolor='#e4007f', s=50, label="train data") plt.scatter(X_test, y_test, facecolor="none", edgecolor='#f19ec2', s=50, label="test data") plt.plot(X_underlying, y_underlying, c='#000000', label=r"underlying distribution") plt.legend(fontsize='x-large') # 給圖像加圖例 plt.savefig('ml-vis.pdf') # 保存圖像到PDF文件中 plt.show()

輸出結果:

2.2.2 模型構建

import torch torch.seed() # 設置隨機種子 class Op(object):def __init__(self):passdef __call__(self, inputs):return self.forward(inputs)def forward(self, inputs):raise NotImplementedErrordef backward(self, inputs):raise NotImplementedError # 線性算子 class Linear(Op):def __init__(self, input_size):"""輸入:- input_size:模型要處理的數據特征向量長度"""self.input_size = input_size# 模型參數self.params = {}self.params['w'] = torch.randn(self.input_size, 1)self.params['b'] = torch.zeros([1])def __call__(self, X):return self.forward(X)# 前向函數def forward(self, X):N, D = X.shapeif self.input_size == 0:return torch.full(shape=[N, 1], fill_value=self.params['b'])assert D == self.input_size # 輸入數據維度合法性驗證# 使用torch.matmul計算兩個tensor的乘積y_pred = torch.matmul(X, self.params['w']) + self.params['b']return y_pred# 注意這里我們為了和后面章節統一,這里的X矩陣是由N個x向量的轉置拼接成的,與原教材行向量表示方式不一致 input_size = 3 N = 2 X = torch.randn(N,input_size) # 生成2個維度為3的數據 model = Linear(input_size) y_pred = model(X) print("y_pred:", y_pred) # 輸出結果的個數也是2個

y_pred: tensor([[ 0.7946],
[-3.1321]])

2.2.3 損失函數

回歸任務中常用的評估指標是均方誤差
均方誤差(mean-square error, MSE)是反映估計量與被估計量之間差異程度的一種度量。

import torch # 損失函數 def mean_squared_error(y_true, y_pred):"""輸入:- y_true: tensor,樣本真實標簽- y_pred: tensor, 樣本預測標簽輸出:- error: float,誤差值"""assert y_true.shape[0] == y_pred.shape[0]# torch.square計算輸入的平方值# torch.mean沿 axis 計算 x 的平均值,默認axis是None,則對輸入的全部元素計算平均值。error = torch.mean(torch.square(y_true - y_pred))return error# 構造一個簡單的樣例進行測試:[N,1], N=2 y_true = torch.tensor([[-0.2], [4.9]], dtype=torch.float32) y_pred = torch.tensor([[1.3], [2.5]], dtype=torch.float32)error = mean_squared_error(y_true=y_true, y_pred=y_pred).item() print("error:", error) error: 4.005000114440918

思考:沒有除2合理么?談談自己的看法,寫到實驗報告。
合理,2只是一個常數,它不會影響均方誤差的準確性。

2.2.4 模型優化

經驗風險 ( Empirical Risk ),即在訓練集上的平均損失。

#模型優化 def optimizer_lsm(model, X, y, reg_lambda=0):"""輸入:- model: 模型- X: tensor, 特征數據,shape=[N,D]- y: tensor,標簽數據,shape=[N]- reg_lambda: float, 正則化系數,默認為0輸出:- model: 優化好的模型"""N, D = X.shape# 對輸入特征數據所有特征向量求平均x_bar_tran = torch.mean(X, axis=0).T# 求標簽的均值,shape=[1]y_bar = torch.mean(y)# paddle.subtract通過廣播的方式實現矩陣減向量x_sub = torch.subtract(X, x_bar_tran)# 使用paddle.all判斷輸入tensor是否全0if torch.all(x_sub == 0):model.params['b'] = y_barmodel.params['w'] = torch.zeros(shape=[D])return model# paddle.inverse求方陣的逆tmp = torch.inverse( torch.matmul(x_sub.T, x_sub) +reg_lambda * torch.eye(num_rows=(D)))w = torch.matmul(torch.matmul(tmp, x_sub.T), (y - y_bar))b = y_bar - torch.matmul(x_bar_tran, w)model.params['b'] = bmodel.params['w'] = torch.squeeze(w, axis=-1)return model

思考1. 為什么省略1/N了不影響效果?
它是個系數,對于求解優化的問題,它不會造成優化結果的改變。

思考 2. 什么是最小二乘法( Least Square Method , LSM )
最小二乘法主要是通過最小化誤差的平方和尋找數據的最佳函數匹配。

2.2.5 模型訓練

在準備了數據、模型、損失函數和參數學習的實現之后,我們開始模型的訓練。在回歸任務中,模型的評價指標和損失函數一致,都為均方誤差。
通過上文實現的線性回歸類來擬合訓練數據,并輸出模型在訓練集上的損失。

input_size = 1 model = Linear(input_size) model = optimizer_lsm(model,X_train.reshape([-1,1]),y_train.reshape([-1,1])) print("w_pred:",model.params['w'].item(), "b_pred: ", model.params['b'].item())y_train_pred = model(X_train.reshape([-1,1])).squeeze() train_error = mean_squared_error(y_true=y_train, y_pred=y_train_pred).item() print("train error: ",train_error)

w_pred: 1.2005535364151 b_pred: 0.6063849329948425
train error: 5.124952793121338

2.2.6 模型評估

下面用訓練好的模型預測一下測試集的標簽,并計算在測試集上的損失。

y_test_pred = model(X_test.reshape([-1,1])).squeeze() test_error = mean_squared_error(y_true=y_test, y_pred=y_test_pred).item() print("test error: ",test_error)

test error: 3.3286447525024414

2.2.7 樣本數量 & 正則化系數

1.調整訓練數據的樣本數量,由 100 調整到 5000,觀察對模型性能的影響。

train error: 4.095644950866699

2.調整正則化系數,觀察對模型性能的影響。

train error: 4.089155673980713

2.3 多項式回歸

2.3.1 數據集構建

import math import torch import matplotlib.pyplot as plt import numpy as np # sin函數: sin(2 * pi * x) def sin(x):y =torch.sin(2 * math.pi * x)return y def create_toy_data(func, interval, sample_num, noise = 0.0, add_outlier = False, outlier_ratio = 0.001):# 均勻采樣# 使用torch.rand在生成sample_num個隨機數X = torch.rand(size = [sample_num]) * (interval[1]-interval[0]) + interval[0]y = func(X)# 生成高斯分布的標簽噪聲# 使用torch.normal生成0均值,noise標準差的數據epsilon = torch.tensor(np.random.normal(0,noise,size=y.shape[0]))y = y + epsilonif add_outlier: # 生成額外的異常點outlier_num = int(len(y)*outlier_ratio)if outlier_num != 0:# 使用torch.randint生成服從均勻分布的、范圍在[0, len(y))的隨機Tensoroutlier_idx = torch.rand(len(y),shape = [outlier_num])y[outlier_idx] = y[outlier_idx] * 5return X, y func = sin interval = (0,1) train_num = 15 test_num = 10 noise = 0.5 #0.1 X_train, y_train = create_toy_data(func=func, interval=interval, sample_num=train_num, noise = noise) X_test, y_test = create_toy_data(func=func, interval=interval, sample_num=test_num, noise = noise)X_underlying = torch.linspace(interval[0],interval[1],steps=100) y_underlying = sin(X_underlying)# 繪制圖像 plt.rcParams['figure.figsize'] = (8.0, 6.0) plt.scatter(X_train, y_train, facecolor="none", edgecolor='#e4007f', s=50, label="train data") #plt.scatter(X_test, y_test, facecolor="none", edgecolor="r", s=50, label="test data") plt.plot(X_underlying, y_underlying, c='#000000', label=r"$\sin(2\pi x)$") plt.legend(fontsize='x-large') plt.savefig('ml-vis2.pdf') plt.show()

2.3.2 模型構建

套用求解線性回歸參數的方法來求解多項式回歸參數

import torch # 多項式轉換 def polynomial_basis_function(x, degree=2):if degree == 0:return torch.ones(shape=x.shape, dtype=torch.loat32)x_tmp = xx_result = x_tmpfor i in range(2, degree + 1):x_tmp = torch.multiply(x_tmp, x) # 逐元素相乘x_result = torch.concat((x_result, x_tmp), axis=-1)return x_result# 簡單測試 data = [[2], [3], [4]] X = torch.as_tensor(data=data, dtype=torch.float32) degree = 3 transformed_X = polynomial_basis_function(X, degree=degree) print("轉換前:", X) print("階數為", degree, "轉換后:", transformed_X)

轉換前: tensor([[2.],
[3.],
[4.]])
階數為 3 轉換后: tensor([[ 2., 4., 8.],
[ 3., 9., 27.],
[ 4., 16., 64.]])

2.3.3 模型訓練

對于多項式回歸,可以同樣使用前面線性回歸中定義的LinearRegression算子、訓練函數train、均方誤差函數mean_squared_error。

for i, degree in enumerate([0, 1, 3, 8]): # []中為多項式的階數model = Linear(degree)X_train_transformed = polynomial_basis_function(X_train.reshape([-1, 1]), degree)X_underlying_transformed = polynomial_basis_function(X_underlying.reshape([-1, 1]), degree)model = optimizer_lsm(model, X_train_transformed, y_train.reshape([-1, 1])) # 擬合得到參數y_underlying_pred = model(X_underlying_transformed).squeeze()print(model.params)# 繪制圖像plt.subplot(2, 2, i + 1)plt.scatter(X_train, y_train, facecolor="none", edgecolor='#e4007f', s=50, label="train data")plt.plot(X_underlying, y_underlying, c='#000000', label=r"$\sin(2\pi x)$")plt.plot(X_underlying, y_underlying_pred, c='#f19ec2', label="predicted function")plt.ylim(-2, 1.5)plt.annotate("M={}".format(degree), xy=(0.95, -1.4))# plt.legend(bbox_to_anchor=(1.05, 0.64), loc=2, borderaxespad=0.) plt.legend(loc='lower left', fontsize='x-large') plt.savefig('ml-vis3.pdf') plt.show()

2.3.4 模型評估

# 訓練誤差和測試誤差 training_errors = [] test_errors = [] distribution_errors = []# 遍歷多項式階數 for i in range(9):model = Linear(i)X_train_transformed = polynomial_basis_function(X_train.reshape([-1, 1]), i)X_test_transformed = polynomial_basis_function(X_test.reshape([-1, 1]), i)X_underlying_transformed = polynomial_basis_function(X_underlying.reshape([-1, 1]), i)optimizer_lsm(model, X_train_transformed, y_train.reshape([-1, 1]))y_train_pred = model(X_train_transformed).squeeze()y_test_pred = model(X_test_transformed).squeeze()y_underlying_pred = model(X_underlying_transformed).squeeze()train_mse = mean_squared_error(y_true=y_train, y_pred=y_train_pred).item()training_errors.append(train_mse)test_mse = mean_squared_error(y_true=y_test, y_pred=y_test_pred).item()test_errors.append(test_mse)# distribution_mse = mean_squared_error(y_true=y_underlying, y_pred=y_underlying_pred).item()# distribution_errors.append(distribution_mse)print("train errors: \n", training_errors) print("test errors: \n", test_errors) # print ("distribution errors: \n", distribution_errors)# 繪制圖片 plt.rcParams['figure.figsize'] = (8.0, 6.0) plt.plot(training_errors, '-.', mfc="none", mec='#e4007f', ms=10, c='#e4007f', label="Training") plt.plot(test_errors, '--', mfc="none", mec='#f19ec2', ms=10, c='#f19ec2', label="Test") # plt.plot(distribution_errors, '-', mfc="none", mec="#3D3D3F", ms=10, c="#3D3D3F", label="Distribution") plt.legend(fontsize='x-large') plt.xlabel("degree") plt.ylabel("MSE") plt.savefig('ml-mse-error.pdf') plt.show()

對于模型過擬合的情況,可以引入正則化方法.

degree = 8 # 多項式階數 reg_lambda = 0.0001 # 正則化系數X_train_transformed = polynomial_basis_function(X_train.reshape([-1,1]), degree) X_test_transformed = polynomial_basis_function(X_test.reshape([-1,1]), degree) X_underlying_transformed = polynomial_basis_function(X_underlying.reshape([-1,1]), degree)model = Linear(degree) optimizer_lsm(model,X_train_transformed,y_train.reshape([-1,1]))y_test_pred=model(X_test_transformed).squeeze() y_underlying_pred=model(X_underlying_transformed).squeeze()model_reg = Linear(degree) optimizer_lsm(model_reg,X_train_transformed,y_train.reshape([-1,1]),reg_lambda=reg_lambda)y_test_pred_reg=model_reg(X_test_transformed).squeeze() y_underlying_pred_reg=model_reg(X_underlying_transformed).squeeze()mse = mean_squared_error(y_true = y_test, y_pred = y_test_pred).item() print("mse:",mse) mes_reg = mean_squared_error(y_true = y_test, y_pred = y_test_pred_reg).item() print("mse_with_l2_reg:",mes_reg)# 繪制圖像 plt.scatter(X_train, y_train, facecolor="none", edgecolor="#e4007f", s=50, label="train data") plt.plot(X_underlying, y_underlying, c='#000000', label=r"$\sin(2\pi x)$") plt.plot(X_underlying, y_underlying_pred, c='#e4007f', linestyle="--", label="$deg. = 8$") plt.plot(X_underlying, y_underlying_pred_reg, c='#f19ec2', linestyle="-.", label="$deg. = 8, \ell_2 reg$") plt.ylim(-1.5, 1.5) plt.annotate("lambda={}".format(reg_lambda), xy=(0.82, -1.4)) plt.legend(fontsize='large') plt.savefig('ml-vis4.pdf') plt.show()

2.4 Runner類介紹

機器學習方法流程包括數據集構建、模型構建、損失函數定義、優化器、模型訓練、模型評價、模型預測等環節。

為了更方便地將上述環節規范化,我們將機器學習模型的基本要素封裝成一個Runner類。

除上述提到的要素外,再加上模型保存、模型加載等功能。

Runner類的成員函數定義如下:

__init__函數:實例化Runner類,需要傳入模型、損失函數、優化器和評價指標等;
train函數:模型訓練,指定模型訓練需要的訓練集和驗證集;
evaluate函數:通過對訓練好的模型進行評價,在驗證集或測試集上查看模型訓練效果;
predict函數:選取一條數據對訓練好的模型進行預測;
save_model函數:模型在訓練過程和訓練結束后需要進行保存;
load_model函數:調用加載之前保存的模型。

class Runner(object):def __init__(self, model, optimizer, loss_fn, metric):self.model = model # 模型self.optimizer = optimizer # 優化器self.loss_fn = loss_fn # 損失函數 self.metric = metric # 評估指標# 模型訓練def train(self, train_dataset, dev_dataset=None, **kwargs):pass# 模型評價def evaluate(self, data_set, **kwargs):pass# 模型預測def predict(self, x, **kwargs):pass# 模型保存def save_model(self, save_path):pass# 模型加載def load_model(self, model_path):pass # optimizer_lsm def optimizer_lsm(model, X, y, reg_lambda=0):N, D = X.shape# 對輸入特征數據所有特征向量求平均x_bar_tran = torch.mean(X, axis=0).T# 求標簽的均值,shape=[1]y_bar = torch.mean(y)# torch.subtract通過廣播的方式實現矩陣減向量x_sub = torch.subtract(X, x_bar_tran)# 使用torch.all判斷輸入tensor是否全0if torch.all(x_sub == 0):model.params['b'] = y_barmodel.params['w'] = torch.zeros(shape=[D])return model# torch.inverse求方陣的逆tmp = torch.inverse(torch.matmul(x_sub.T, x_sub) +reg_lambda * torch.eye(D))w = torch.matmul(torch.matmul(tmp, x_sub.T), (y - y_bar))b = y_bar - torch.matmul(x_bar_tran, w)model.params['b'] = bmodel.params['w'] = torch.squeeze(w, axis=-1)return model

2.5 基于線性回歸的波士頓房價預測

使用線性回歸來對馬薩諸塞州波士頓郊區的房屋進行預測。

實驗流程主要包含如下5個步驟:

數據處理:包括數據清洗(缺失值和異常值處理)、數據集劃分,以便數據可以被模型正常讀取,并具有良好的泛化性;
模型構建:定義線性回歸模型類;
訓練配置:訓練相關的一些配置,如:優化算法、評價指標等;
組裝訓練框架Runner:Runner用于管理模型訓練和測試過程;
模型訓練和測試:利用Runner進行模型訓練和測試。

2.5.1 數據處理

2.5.1.1 數據清洗

import pandas as pd # 開源數據分析和操作工具# 利用pandas加載波士頓房價的數據集 data=pd.read_csv(r"C:\Users\320\Documents\Tencent Files\1377916621\FileRecv/boston_house_prices.csv") # 查看各字段缺失值統計情況 print(data.isna().sum())

從輸出結果看,波士頓房價預測數據集中不存在缺失值的情況。
異常值處理:通過箱線圖直觀的顯示數據分布,并觀測數據中的異常值。箱線圖一般由五個統計值組成:最大值、上四分位、中位數、下四分位和最小值。一般來說,觀測到的數據大于最大估計值或者小于最小估計值則判斷為異常值,其中:
最大估計值=上四分位+1.5?(上四分位?下四分位)
最小估計值=下四分位?1.5?(上四分位?下四分位)

import matplotlib.pyplot as plt # 可視化工具# 箱線圖查看異常值分布 def boxplot(data, fig_name):# 繪制每個屬性的箱線圖data_col = list(data.columns)# 連續畫幾個圖片plt.figure(figsize=(5, 5), dpi=300)# 子圖調整plt.subplots_adjust(wspace=0.6)# 每個特征畫一個箱線圖for i, col_name in enumerate(data_col):plt.subplot(3, 5, i+1)# 畫箱線圖plt.boxplot(data[col_name], showmeans=True, meanprops={"markersize":1,"marker":"D","markeredgecolor":'#f19ec2'}, # 均值的屬性medianprops={"color":'#e4007f'}, # 中位數線的屬性whiskerprops={"color":'#e4007f', "linewidth":0.4, 'linestyle':"--"},flierprops={"markersize":0.4},) # 圖名plt.title(col_name, fontdict={"size":5}, pad=2)# y方向刻度plt.yticks(fontsize=4, rotation=90)plt.tick_params(pad=0.5)# x方向刻度plt.xticks([])plt.savefig(fig_name)plt.show()boxplot(data, 'ml-vis5.pdf')

使用四分位值篩選出箱線圖中分布的異常值,并將這些數據視為噪聲,其將被臨界值取代,代碼實現如下:

# 四分位處理異常值 num_features=data.select_dtypes(exclude=['object','bool']).columns.tolist()for feature in num_features:if feature =='CHAS':continueQ1 = data[feature].quantile(q=0.25) # 下四分位Q3 = data[feature].quantile(q=0.75) # 上四分位IQR = Q3-Q1 top = Q3+1.5*IQR # 最大估計值bot = Q1-1.5*IQR # 最小估計值values=data[feature].valuesvalues[values > top] = top # 臨界值取代噪聲values[values < bot] = bot # 臨界值取代噪聲data[feature] = values.astype(data[feature].dtypes)# 再次查看箱線圖,異常值已被臨界值替換(數據量較多或本身異常值較少時,箱線圖展示會不容易體現出來) boxplot(data, 'ml-vis6.pdf')

2.5.1.2 數據集劃分

import torch torch.seed() # 劃分訓練集和測試集 def train_test_split(X, y, train_percent=0.8):n = len(X)shuffled_indices = torch.randperm(n) # 返回一個數值在0到n-1、隨機排列的1-D Tensortrain_set_size = int(n*train_percent)train_indices = shuffled_indices[:train_set_size]test_indices = shuffled_indices[train_set_size:]X = X.valuesy = y.valuesX_train=X[train_indices]y_train = y[train_indices]X_test = X[test_indices]y_test = y[test_indices]return X_train, X_test, y_train, y_test X = data.drop(['MEDV'], axis=1) y = data['MEDV'] X_train, X_test, y_train, y_test = train_test_split(X,y)# X_train每一行是個樣本,shape[N,D]

2.5.1.3特征工程

import torch X_train = torch.tensor(X_train ,dtype=torch.float32) X_test = torch.tensor(X_test ,dtype=torch.float32) y_train = torch.tensor(y_train ,dtype=torch.float32) y_test = torch.tensor(y_test ,dtype=torch.float32) X_min = torch.min(X_train) X_max = torch.max(X_train) X_train = (X_trai n -X_min ) /(X_ma x -X_min) X_test = (X_tes t -X_min ) /(X_ma x -X_min) # 訓練集構造 train_datase t =(X_train ,y_train) # 測試集構造 test_datase t =(X_test ,y_test) print(train_dataset) print(test_dataset)

2.5.2 模型構建

from nndl.op import Linear # 模型實例化 input_size = 12 model=Linear(input_size) # nndl.op import torch torch.seed() # 設置隨機種子 class Op(object):def __init__(self):passdef __call__(self, inputs):return self.forward(inputs)def forward(self, inputs):raise NotImplementedErrordef backward(self, inputs):raise NotImplementedError # 線性算子 class Linear(Op):def __init__(self, input_size):self.input_size = input_size# 模型參數self.params = {}self.params['w'] = torch.randn(self.input_size, 1)self.params['b'] = torch.zeros([1])def __call__(self, X):return self.forward(X)# 前向函數def forward(self, X):N, D = X.shapeif self.input_size == 0:return torch.full(shape=[N, 1], fill_value=self.params['b'])assert D == self.input_size # 輸入數據維度合法性驗證# 使用torch.matmul計算兩個tensor的乘積y_pred = torch.matmul(X, self.params['w']) + self.params['b']sreturn y_pred

2.5.3 完善Runner類

模型定義好后,圍繞模型需要配置損失函數、優化器、評估、測試等信息,以及模型相關的一些其他信息(如模型存儲路徑等)。
在本章中使用的Runner類為V1版本。其中訓練過程通過直接求解解析解的方式得到模型參數,沒有模型優化及計算損失函數過程,模型訓練結束后保存模型參數。
訓練配置中定義:
訓練環境,如GPU還是CPU,本案例不涉及;
優化器,本案例不涉及;
損失函數,本案例通過平方損失函數得到模型參數的解析解;
評估指標,本案例利用MSE評估模型效果。
在測試集上使用MSE對模型性能進行評估。

import torch.nn as nn mse_loss = nn.MSELoss() import torch import os from nndl.opitimizer import optimizer_lsm class Runner(object):def __init__(self, model, optimizer, loss_fn, metric):# 優化器和損失函數為None,不再關注# 模型self.model = model# 評估指標self.metric = metric# 優化器self.optimizer = optimizerdef train(self, dataset, reg_lambda, model_dir):X, y = datasetself.optimizer(self.model, X, y, reg_lambda)# 保存模型self.save_model(model_dir)def evaluate(self, dataset, **kwargs):X, y = datasety_pred = self.model(X)result = self.metric(y_pred, y)return resultdef predict(self, X, **kwargs):return self.model(X)def save_model(self, model_dir):if not os.path.exists(model_dir):os.makedirs(model_dir)params_saved_path = os.path.join(model_dir, 'params.pdtensor')torch.save(model.params, params_saved_path)def load_model(self, model_dir):params_saved_path = os.path.join(model_dir, 'params.pdtensor')self.model.params = torch.load(params_saved_path)optimizer = optimizer_lsm# 實例化Runner runner = Runner(model, optimizer=optimizer, loss_fn=None, metric=mse_loss) # optimizer_lsm def optimizer_lsm(model, X, y, reg_lambda=0):N, D = X.shape# 對輸入特征數據所有特征向量求平均x_bar_tran = torch.mean(X, axis=0).T# 求標簽的均值,shape=[1]y_bar = torch.mean(y)# torch.subtract通過廣播的方式實現矩陣減向量x_sub = torch.subtract(X, x_bar_tran)# 使用torch.all判斷輸入tensor是否全0if torch.all(x_sub == 0):model.params['b'] = y_barmodel.params['w'] = torch.zeros(shape=[D])return model# torch.inverse求方陣的逆tmp = torch.inverse(torch.matmul(x_sub.T, x_sub) +reg_lambda * torch.eye(num_rows=(D)))w = torch.matmul(torch.matmul(tmp, x_sub.T), (y - y_bar))b = y_bar - torch.matmul(x_bar_tran, w)model.params['b'] = bmodel.params['w'] = torch.squeeze(w, axis=-1)return model

2.5.4 模型訓練

在組裝完成Runner之后,我們將開始進行模型訓練、評估和測試。首先,我們先實例化Runner,然后開始進行裝配訓練環境,接下來就可以開始訓練了,相關代碼如下:

# 模型保存文件夾 saved_dir = '/home/aistudio/work/models'# 啟動訓練 runner.train(train_dataset,reg_lambda=0,model_dir=saved_dir)

打印出訓練得到的權重:

columns_list = data.columns.to_list() weights = runner.model.params['w'].tolist() b = runner.model.params['b'].item()for i in range(len(weights)):print(columns_list[i],"weight:",weights[i])print("b:",b)

CRIM weight: -545.2598266601562
ZN weight: 22.065792083740234
INDUS weight: -8.44003677368164
CHAS weight: 1530.1904296875
NOX weight: -8955.4931640625
RM weight: 2347.146484375
AGE weight: -5.644048690795898
DIS weight: -707.4413452148438
RAD weight: 271.2647705078125
TAX weight: -6.599415302276611
PTRATIO weight: -592.3955688476562
LSTAT weight: -313.0545654296875
b: 35.074371337890625

2.5.5 模型測試

# 加載模型權重 runner.load_model(saved_dir) mse = runner.evaluate(test_dataset) print('MSE:', mse.item())

MSE: 14.380061149597168

2.5.6 模型預測

runner.load_model(saved_dir) pred = runner.predict(X_test[:1]) print("真實房價:",y_test[:1].item()) print("預測的房價:",pred.item())

真實房價: 10.899999618530273
預測的房價: 15.171201705932617

問題1:使用類實現機器學習模型的基本要素有什么優點?
(1)可以提高程序的效率。
(2)易維護:如果需要改變需求,那么也只需要在局部模塊進行維護,大大降低了成本。
(3)易擴展:系統更靈活、更容易擴展,而且成本較低。

問題2:算子op、優化器opitimizer放在單獨的文件中,主程序在使用時調用該文件。這樣做有什么優點?
簡潔明了,需要修改時,只需要改其中一部分,不需要進行全篇改動。
問題3:線性回歸通常使用平方損失函數,能否使用交叉熵損失函數?為什么?
不能,平方損失函數看重每一個輸出結果,交叉熵損失函數只看重正確的結果。因此平方損失函數還和錯誤結果有關,所以平方損失函數會使正確的分類變大,還會讓錯誤分類都變得更加平均,但實際中后面得調整是沒必要的,但是對于回歸問題這樣的考慮是比較重要得,因而回歸問題上使用交叉熵不合適。

總結

以上是生活随笔為你收集整理的NNDL 实验三 线性回归的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。