動手學深度學習(tensorflow)---學習筆記整理(二、線性迴歸篇)

(有關公式、基本理論等大量內容摘自《動手學深度學習》(TF2.0版)

首先說一下線性迴歸是什麼?

個人理解:線性迴歸利用某種方法來確定兩種或兩種以上變量間相互依賴的定量關係的一種分析方法。簡單點說,現在有一些隨機點,通過某種方法找出來他所符合的線性方程。用途嘛~例如,我們有房價和決定因素的相關數據,可以找到它符合的線性函數,然後對房價進行預測~

線性迴歸的基本要素

簡單點說就是自變量們和因變量。用一個例子來說,以一個房屋價格預測爲例,這個應用的目標是預測一棟房子的售出價格(元)。我們知道這個價格取決於很多因素,如房屋狀況、地段、市場行情等。爲了簡單起見,這裏我們假設價格只取決於房屋狀況的兩個因素,即面積(平方米)和房齡(年)。接下來我們希望探索價格與這兩個因素的具體關係。

模型的定義

模型的訓練

模型的訓練指通過利用數據來尋找特定的神經網絡模型的參數值,使模型在數據上的誤差儘可能小。主要有以下三個方面的內容。

(1)訓練數據

(2)損失函數

(3)優化算法

模型的預測

爲什麼學習深度學習要先學習線性迴歸?

線性迴歸可以理解成一層的神經網絡,是最簡單的神經網絡,學會線性迴歸可以更好的學習更深層的網絡。以上述房價預測爲例的線性迴歸的神經網絡如下圖所示:

輸入分別爲 x1​ 和 x2,因此輸入層的輸入個數爲2。輸入個數也叫特徵數或特徵向量維度。輸出結果爲y,所以輸出層的輸出個數爲1。

∇劈形算符,就是對倒三角後面的量做如下操作:表示對函數在各個正交方向上求導數以後再分別乘上各個方向上的單位向量

(相關公式直接抄自開頭所提到的開源資料中,有興趣大家可以推導試試)

線性迴歸的從零開始實現

(各種解釋都在代碼中了,代碼源自引文~)

import tensorflow as tf
print(tf.__version__)
from matplotlib import pyplot as plt
import random
num_inputs = 2
num_examples = 1000
#實際參數
true_w = [2, -3.4]
true_b = 4.2
#1.x版本
#sess = tf.Session()
#2.0版本
sess=tf.compat.v1.Session()
# tf.random_normal()函數用於從“服從指定正態分佈的序列”中隨機取出指定個數的值。
# tf.random_normal(shape, mean=0.0, stddev=1.0, dtype=tf.float32, seed=None, name=None)
# shape: 輸出張量的形狀,必選
# mean: 正態分佈的均值,默認爲0
# stddev: 正態分佈的標準差,默認爲1.0
# dtype: 輸出的類型,默認爲tf.float32
# seed: 隨機數種子,是一個整數,當設置之後,每次生成的隨機數都一樣
# name: 操作的名稱

#1.x:random_normal
features = tf.random.normal((num_examples, num_inputs),stddev = 1)
labels = true_w[0] * features[:,0] + true_w[1] * features[:,1] + true_b
labels += tf.random.normal(labels.shape,stddev=0.01)

#隨機返回batch_size個隨機個樣例+特徵值
def data_iter(batch_size, features, labels):
    #num_examples = len(features)
    num_examples = features.shape[0]
    indices = list(range(num_examples))
    #shuffle() 方法將序列的所有元素隨機排序
    random.shuffle(indices)
    for i in range(0, num_examples, batch_size):
        #以batch_size=10爲例子, 將數據劃分爲0 - 10, 10 - 20, 20 - 30等
        j = indices[i: min(i+batch_size, num_examples)]
        #params,按照axis座標和indices標註的元素下標,把這些元素抽取出來組成新的tensor.
        #yield的作用: 就是把一個函數變成一個生成器,調用函數時候,輸出返回的是一個iterable對象。,yield使得代碼簡潔的同時, 降低了計算消耗
        yield tf.gather(features, axis=0, indices=j), tf.gather(labels, axis=0, indices=j)
#隨機樣本數量
batch_size = 10
#提取隨機樣本並輸出看看
for X, y in data_iter(batch_size, features, labels):
    print(X, y)
    break
#初始化w、b,將權重初始化成均值爲0、標準差爲0.01的正態隨機數,偏差則初始化成0
w = tf.Variable(tf.random.normal((num_inputs, 1), stddev=0.01))
b = tf.Variable(tf.zeros((1,)))
#定義模型,其實就是矩陣乘法
def linreg(X, w, b):
    return tf.matmul(X, w) + b
#定義損失函數,y_hat爲預測值,y爲實際值
def squared_loss(y_hat, y):
    return (y_hat - tf.reshape(y, y_hat.shape)) ** 2 /2
#優化函數,此方法爲小批量隨機梯度下降算法。它通過不斷迭代模型參數來優化損失函數,這裏自動求梯度模塊計算得來的梯度是一個批量樣本的梯度和,我們將它除以批量大小來得到平均值
def sgd(params, lr, batch_size, grads):
    """Mini-batch stochastic gradient descent."""
    for i, param in enumerate(params):
        param.assign_sub(lr * grads[i] / batch_size)
#lr學習率,num_epochs訓練輪數,net模型名稱,loss損失函數名
lr = 0.03
num_epochs = 30
net = linreg
loss = squared_loss

for epoch in range(num_epochs):
    for X, y in data_iter(batch_size, features, labels):
        with tf.GradientTape() as t:
            #w、b由tf.Variable創建,無需watch,如果爲tf.constant或者tf.convert_to_tensor創建則需要watch
            #t.watch([w,b])
            #計算損失
            l = loss(net(X, w, b), y)
        #計算梯度
        grads = t.gradient(l, [w, b])
        #使用優化算法
        sgd([w, b], lr, batch_size, grads)
    #train_l是全部的損失值的數組,1000*1
    train_l = loss(net(features, w, b), labels)
    #print(train_l)
    #print(train_l.shape)
    print('epoch %d, loss %f' % (epoch + 1, tf.reduce_mean(train_l)))
print(true_w, w)
print(true_b, b)

線性迴歸的簡潔實現

(各種解釋都在代碼中了,代碼源自引文~)

import tensorflow as tf

num_inputs = 2
num_examples = 1000
true_w = [2, -3.4]
true_b = 4.2
features = tf.random.normal(shape=(num_examples, num_inputs), stddev=1)
labels = true_w[0] * features[:, 0] + true_w[1] * features[:, 1] + true_b
labels += tf.random.normal(labels.shape, stddev=0.01)

from tensorflow import data as tfdata

batch_size = 10
# Dataset.from_tensor_slices,將訓練數據的特徵和標籤組合
dataset = tfdata.Dataset.from_tensor_slices((features, labels))
# 隨機讀取小批量
#shuffle 的 buffer_size 參數應大於等於樣本數
dataset = dataset.shuffle(buffer_size=num_examples)
#batch 可以指定 batch_size 的分割大小,分割爲10個10個的小組
dataset = dataset.batch(batch_size)

#輸出分組看看
#enumerate() 函數用於將一個可遍歷的數據對象(如列表、元組或字符串)組合爲一個索引序列,同時列出數據和數據下標,返回值爲索引值+遍歷對象的對應索引內容
for (batch, (X, y)) in enumerate(dataset):
    print(X, y)
    break
#Tensorflow 2.0推薦使用Keras定義網絡
from tensorflow import keras
from tensorflow.keras import layers
from tensorflow import initializers as init
model = keras.Sequential()
#Keras 中初始化參數由 kernel_initializer 和 bias_initializer 選項分別設置權重和偏置的初始化方式。
# RandomNormal(stddev=0.01)指定權重參數每個元素將在初始化時隨機採樣於均值爲0、標準差爲0.01的正態分佈。
# bias_initializer不設置,偏差參數默認會初始化爲零。
model.add(layers.Dense(1, kernel_initializer=init.RandomNormal(stddev=0.01)))
#設置損失函數,均方誤差損失
from tensorflow import losses
loss = losses.MeanSquaredError()
#設置優化器及學習率,小批量隨機梯度下降(SGD)
from tensorflow.keras import optimizers
trainer = optimizers.SGD(learning_rate=0.1)
#訓練次數
num_epochs = 30
#在使用Tensorflow訓練模型時,我們通過調用tensorflow.GradientTape記錄動態圖梯度,執行tape.gradient獲得動態圖中各變量梯度。
# 通過 model.trainable_variables 找到需要更新的變量,並用 trainer.apply_gradients 更新權重,完成一步訓練
for epoch in range(1, num_epochs + 1):
    for (batch, (X, y)) in enumerate(dataset):
        with tf.GradientTape() as tape:
            l = loss(model(X, training=True), y)
        #model.trainable_variables會獲得需要用的量
        #獲得梯度
        grads = tape.gradient(l, model.trainable_variables)
        #apply_gradients更新權重
        trainer.apply_gradients(zip(grads, model.trainable_variables))

    l = loss(model(features), labels)
    print('epoch %d, loss: %f' % (epoch, l))

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章