李沐動手學深度學習V2-chap_preliminaries

猫猫不会吃芋头發表於2024-07-02

李沐動手學深度學習V2

文章內容說明

本文主要是自己學習過程中的隨手筆記,需要自取
課程參考B站https://space.bilibili.com/1567748478?spm_id_from=333.788.0.0
課件等資訊原影片簡介中有


CSV檔案修改讀取成張量tensor

資料預處理

首先(建立一個人工資料集,並儲存在CSV(逗號分隔值)檔案) ../data/house_tiny.csv中,在csv檔案中寫入資料

import os

os.makedirs(os.path.join('..', 'data'), exist_ok=True)
data_file = os.path.join('..', 'data', 'house_tiny.csv')
with open(data_file, 'w') as f:
    f.write('NumRooms,Alley,Price\n')  # 列名
    f.write('NA,Pave,127500\n')  # 每行表示一個資料樣本
    f.write('2,NA,106000\n')
    f.write('4,NA,178100\n')
    f.write('NA,NA,140000\n')

要進行讀取資料集,我們匯入pandas包並呼叫read_csv函式。該資料集有四行三列。其中每行描述了房間數量(“NumRooms”)、巷子型別(“Alley”)和房屋價格(“Price”)。

# 如果沒有安裝pandas,只需取消對以下行的註釋來安裝pandas
# !pip install pandas
import pandas as pd

data = pd.read_csv(data_file)
print(data)

結果如下
image

處理缺失值

為了處理缺失的資料,典型的方法包括插值法和刪除法,下面以插值法作為示例
透過位置索引iloc,我們將data分成inputsoutputs, 其中前者為data的前兩列,而後者為data的最後一列。 對於inputs中缺少的數值,我們用同一列的均值替換“NaN”項。【第二列求不出均值所以不改變】

inputs, outputs = data.iloc[:, 0:2], data.iloc[:, 2]
inputs = inputs.fillna(inputs.mean())
print(inputs)

結果如下
image
對於inputs中的類別值或離散值,我們將“NaN”視為一個類別,使用獨熱編碼,NAN值賦0,其他賦1

inputs = pd.get_dummies(inputs, dummy_na=True)
print(inputs)

結果如下
image

轉為張量格式

現在inputs和outputs中的所有條目都是數值型別,它們可以轉換為張量格式。

import torch

X = torch.tensor(inputs.to_numpy(dtype=float))
y = torch.tensor(outputs.to_numpy(dtype=float))
X, y

結果如下
image
python預設型別float64,這樣比較慢,機器學習一般改float32

範數

向量的範數是表示一個向量有多大。 這裡考慮的大小(size)概念不涉及維度,而是分量的大小。
image

L1範數

L1範數,它表示為向量元素的絕對值之和
image

    # 向量(1維張量)
    vector = torch.tensor([1.0, 2.0, 3.0, 4.0, -5.0])
    # 求向量的L1範數
    norm1 = torch.norm(vector, p=1, dim=0)

結果如下
image

L2範數

L2範數是向量元素平方和的平方根
image
可以用以下程式碼實現

u = torch.tensor([3.0, -4.0]) #或u = torch.tensor([3.0, -4.0],p=2)
torch.norm(u)

結果如下
image

Frobenius範數

Frobenius範數(Frobenius norm)是矩陣元素平方和的平方根
image

#Frobenius範數是矩陣,Lp範數是向量
torch.norm(torch.ones((4, 9)))

結果如下
image

自動求導實現

image
在我們計算y關於x的梯度之前,需要一個地方來儲存梯度。使用如下程式碼

x.requires_grad_(True)  # 等價於x=torch.arange(4.0,requires_grad=True)
x.grad  # 預設值是None

現在計算y

y = 2 * torch.dot(x, x)
y

結果如下
image
透過呼叫反向傳播函式來自動計算y關於x每個分量的梯度

y.backward()
x.grad

結果如下,儲存x每個分量的梯度(即切線/導數)
image
驗證一下是否正確
image

矩陣的反向傳播

image

分離計算

將某些計算移動到記錄的計算圖之外,使用y.detach()將u賦為標量,及x*x

x.grad.zero_()
y = x * x
u = y.detach()
z = u * x
z.sum().backward()
x.grad == u

結果如下
image
由於記錄了y的計算結果,我們可以隨後在y上呼叫反向傳播, 得到y=xx關於的x的導數,即2x

x.grad.zero_()
y.sum().backward()
x.grad == 2 * x

結果如下
image

Python控制流的梯度計算

即使構建函式的計算圖需要透過Python控制流(例如,條件、迴圈或任意函式呼叫),我們仍然可以計算得到的變數的梯度
while迴圈的迭代次數和if語句的結果都取決於輸入a的值

def f(a):
    b = a * 2
    while b.norm() < 1000:
        b = b * 2
    if b.sum() > 0:
        c = b
    else:
        c = 100 * b
    return c

a = torch.randn(size=(), requires_grad=True)
d = f(a)
d.backward()

a.grad == d / a

結果如下
image

相關文章