AI
[AI] 3-4. 實作線性分類器
命題 我們先嘗試製作一個 sample data,考慮某一個台獨國內的房價與坪數的分布,標記出「城市」與「鄉下」兩個標籤: 紫色代表城市的房子、黃色代表鄉下的房子 城市的房子房價較高且坪數較小、鄉下的房子房價較低且坪數較大。 隱藏的特徵為單位坪數的價格,城市會高於鄉下。 以下是產生的 data,seed 設為 42 import numpy as np def load_data(n1=1000,n2=300,seed=42): np.random.seed(seed) n=n1+n2 # 城市房屋 city = np.random.multivariate_normal( mean=[25.9, 1503.7], # [坪數, 總價(萬)] cov=[[33.64, 400], # 坪數標準差 5.8 [400, 64112.24]], # 總價標準差 253.2 size=n ) # 鄉村房屋 rural = np.random.multivariate_normal( mean=[31.8, 834.3], # [坪數, 總價(萬)] cov=[[62.41, 200], # 坪數標準差 7.9 [200, 9623.61]], # 總價標準差 98.1 size=n ) # 合併資料 data = np.vstack((city, rural)).astype(np.float32) labels = np.vstack((np.zeros((n,1), dtype="float32"), np.ones((n,1), dtype="float32"))) # 打散順序 shuffle_idx = np.random.permutation(len(data)) data = data[shuffle_idx] labels = labels[shuffle_idx] # 分割訓練集和測試集 train_data = data[:2*n1] test_data = data[2*n1:] train_labels = labels[:2*n1] test_labels = labels[2*n1:] return (train_data, train_labels), (test_data, test_labels) (train_data, train_labels), (test_data, test_labels) = load_data() 通用函式 因為 price 與 size 之間的值相差較大,所以我定義了正規化的函式、繪圖的函式、training 函式。 import numpy as np import matplotlib.pyplot as plt from sklearn.preprocessing import StandardScaler import tensorflow as tf from tensorflow.keras import layers, models, callbacks import pandas as pd # 共用參數 LEARNING_RATE = 0.01 BATCH_SIZE = 32 EPOCHS = 100 EARLY_STOPPING_PATIENCE = 10 def normalize_data(train_data, test_data): """標準化資料""" scaler = StandardScaler() train_normalized = scaler.fit_transform(train_data) test_normalized = scaler.transform(test_data) return train_normalized, test_normalized, scaler def plot_decision_boundary(model, X, y, title, scaler=None, is_extended=False): """繪製決策邊界 Parameters: ----------- model : 訓練好的模型 X : array-like, shape (n_samples, 2) or (n_samples, 3) 輸入特徵 y : array-like 目標變數 title : str 圖表標題 scaler : StandardScaler, optional 用於還原正規化的scaler is_extended : bool 是否為擴展特徵模型 """ if is_extended: # 對於擴展特徵模型,只使用前兩個特徵繪圖 X_plot = X[:, :2] else: X_plot = X # 設定決策邊界的範圍 x_min, x_max = X_plot[:, 0].min() - 0.5, X_plot[:, 0].max() + 0.5 y_min, y_max = X_plot[:, 1].min() - 0.5, X_plot[:, 1].max() + 0.5 # 創建網格點 xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.02), np.arange(y_min, y_max, 0.02)) # 預測網格點的類別 grid_points = np.c_[xx.ravel(), yy.ravel()] if is_extended: # 為擴展特徵模型添加 price/size ratio ratio = grid_points[:, 1:2] / grid_points[:, 0:1] grid_points = np.hstack((grid_points, ratio)) Z = model.predict(grid_points) Z = Z.reshape(xx.shape) # 如果提供了scaler,將數據轉換回原始尺度 if scaler is not None: # 轉換網格點 grid_points_original = scaler.inverse_transform(grid_points[:, :2]) xx_original = grid_points_original[:, 0].reshape(xx.shape) yy_original = grid_points_original[:, 1].reshape(yy.shape) # 轉換特徵點 X_original = scaler.inverse_transform(X_plot) x_plot = X_original[:, 0] y_plot = X_original[:, 1] xlabel = 'Size' ylabel = 'Price' else: x_plot = X_plot[:, 0] y_plot = X_plot[:, 1] xx_original = xx yy_original = yy xlabel = 'Normalized Size' ylabel = 'Normalized Price' # 繪製圖形 plt.figure(figsize=(10, 8)) # 繪製預測機率的漸層 plt.contourf(xx_original, yy_original, Z, alpha=0.4, levels=np.linspace(0, 1, 11)) # 添加決策邊界(機率=0.5的等高線) plt.contour(xx_original, yy_original, Z, levels=[0.5], colors='red', linestyles='-', linewidths=2) # 繪製資料點 plt.scatter(x_plot, y_plot, c=y.ravel(), alpha=0.8) plt.title(title) plt.xlabel(xlabel) plt.ylabel(ylabel) # 添加顏色條 plt.colorbar(label='Prediction Probability') plt.show() def train_and_evaluate(model, train_data, train_labels, test_data, test_labels, epochs=EPOCHS, batch_size=BATCH_SIZE, use_early_stopping=False): """訓練模型並評估""" callbacks_list = [] if use_early_stopping: early_stopping = callbacks.EarlyStopping( monitor='val_loss', patience=EARLY_STOPPING_PATIENCE, restore_best_weights=True ) callbacks_list.append(early_stopping) history = model.fit( train_data, train_labels, epochs=epochs, batch_size=batch_size, validation_split=0.2, callbacks=callbacks_list, verbose=0 ) test_loss, test_accuracy = model.evaluate(test_data, test_labels, verbose=0) return history, test_loss, test_accuracy 定義模型 本篇設定了不同的 model, optimizer, loss,不代表哪一種 pattern 的優劣,純粹是示範 library 使用。 由於我想看不同的 optimizer/activation/loss function的差異,所以我共用了部分參數 LEARNING_RATE = 0.01 BATCH_SIZE = 32 EPOCHS = 100 EARLY_STOPPING_PATIENCE = 10 我列了幾個不同的策略,來看看結果的差異 SGD + 線性模型 (wx+b) + MSE SGD + Sigmoid 模型 + MSE SGD + ReLU 模型 + MSE SGD + Sigmoid 模型 + BCE Adam + Sigmoid 模型 + BCE Adam + Sigmoid 模型 + BCE (加入新特徵 price/size) Adam + 雜複模型 + BCD Adam + 複雜模型 + BCD (加入新特徵 price/size) 1. SGD + 線性模型 (wx+b) + MSE # 1. 線性模型 (wx+b) def create_linear_model(): model = models.Sequential([ layers.Dense(1, input_shape=(2,), activation='linear') ]) model.compile(optimizer=tf.keras.optimizers.SGD(learning_rate=LEARNING_RATE), loss='mse', metrics=['accuracy']) return model ...
[AI] 3-3. 使用 TensorFlow 與 Keras 函式庫
建立張量與變數 張量 引入 tensorflow import tensorflow as tf 建立張量並以 0 作為初始值 x = tf.ones(shape=(2,1)) print(x) >>> tf.Tensor( [[1.] [1.]], shape=(2, 1), dtype=float32) 建立張量並以 1 作為初始值 y = tf.zeros(shape=(2,1)) print(y) >>> tf.Tensor( [[0.] [0.]], shape=(2, 1), dtype=float32) 建立張量,並以常數初始化 a = tf.constant(((1.,4.),(9.,16.))) print(a) >>> tf.Tensor( [[ 1. 4.] [ 9. 16.]], shape=(2, 2), dtype=float32) 建立由亂數組成的張量 常態分佈 # 亂數從平均值 0、標準差 1 的常態分佈中抽取出來 # 等同於 np.random.normal(size=(3,1), loc=0., scale=1.) z = tf.random.normal(shape=(3,1), mean=0., stddev=1.) print(z) >>> tf.Tensor( [[-1.1859674 ] [ 0.3267818 ] [ 0.11066005]], shape=(3, 1), dtype=float32) 均勻分布 # 亂數從 0 到 1 之間均勻分布抽取出來 # 等同於 np.random.uniform(size=(3,1), low=0., high=1.) k = tf.random.uniform(shape=(3,1), minval=0., maxval=1.) print(k) >>> tf.Tensor( [[0.97643256] [0.13791454] [0.7854562 ]], shape=(3, 1), dtype=float32) NumPy 與 TensorFlow 的差異在於,TensorFlow 的張量為常數 ,無法修改。 將 numpy 轉換成 tensorflow x = np.ones((2,2)) x[0, 0] = 0 y = tf.convert_to_tensor(x) print(y) >>> tf.Tensor( [[0. 1.] [1. 1.]], shape=(2, 2), dtype=float64) 變數 創建變數 直接用 tuple 初始化 v = tf.Variable(((3.,1.,3.),(2.,3.,2.))) print(v) >>> <tf.Variable 'Variable:0' shape=(2, 3) dtype=float32, numpy=array( [[3., 1., 3.], [2., 3., 2.]], dtype=float32)> 用隨機值初始化 v = tf.Variable(initial_value=tf.random.normal(shape=(3,1))) print(v) >>> <tf.Variable 'Variable:0' shape=(3, 1) dtype=float32, numpy=array( [[2.1368823 ], [0.8101084 ], [0.47075817]], dtype=float32)> 變數賦值 透過 assign() 方法對變數賦值 v.assign(tf.ones(shape=(3,1))) >>> <tf.Variable 'UnreadVariable' shape=(3, 1) dtype=float32, numpy=array( [[1.], [1.], [1.]], dtype=float32)> 對變數局部賦值 v[0,0].assign(0) >>> <tf.Variable 'UnreadVariable' shape=(2, 3) dtype=float32, numpy=array( [[0., 1., 3.], [2., 3., 2.]], dtype=float32)> 變數運算 加法 v.assign_add(tf.ones((2,3))) >>> <tf.Variable 'Variable:0' shape=(2, 3) dtype=float32, numpy=array( [[1., 2., 4.], [3., 4., 3.]], dtype=float32)> 減法 v.assign_sub(2*tf.ones((2,3))) >>> <tf.Variable 'UnreadVariable' shape=(2, 3) dtype=float32, numpy=array( [[-1., 0., 2.], [ 1., 2., 1.]], dtype=float32)> 張量操作 基本數學運算 初始化 a = tf.constant(((1.,4.),(9.,16.))) >>> tf.Tensor( [[ 1. 4.] [ 9. 16.]], shape=(2, 2), dtype=float32) 平方 a = tf.square(a) print(a) >>> tf.Tensor( [[ 1. 16.] [ 81. 256.]], shape=(2, 2), dtype=float32) 平方根 a = tf.sqrt(a) print(a) >>> tf.Tensor( [[ 1. 4.] [ 9. 16.]], shape=(2, 2), dtype=float32) 加法(逐元素相加) b = tf.ones((2,2)) print(a+b) >>> tf.Tensor( [[ 2. 5.] [10. 17.]], shape=(2, 2), dtype=float32) 點積 b = tf.constant(((1.,-1.),(-1.,1.))) c = tf.matmul(a,b) print(c) >>> tf.Tensor( [[-3. 3.] [-7. 7.]], shape=(2, 2), dtype=float32) 相乘(逐元素相乘) d = a*b print(d) >>> tf.Tensor( [[ 1. -4.] [-9. 16.]], shape=(2, 2), dtype=float32) 微分 計算一階梯度 ...
[AI] 3-2. Keras 介紹
Keras 是建立在 TensorFlow 上的 Python 深度學習 API,提供了簡易方法來定義、訓練深度學習的模型。 可以想成 TensorFlow 負責張量運算、Keras 是演算法。 Keras(高層 API): 負責深度學習模型的高階抽象 提供用戶友好的介面 處理模型定義和訓練流程 eg. Layer, Model, Optimizers, Loss functions, Metrics TensorFlow(中層): 處理底層的數學運算 管理計算圖和自動微分 優化運算效率 eg. Tensor, Variable、GradientTape Hardware(硬體層): 實際執行計算任務 提供不同的計算加速選項 優化特定類型的運算 eg. CPU, GPU, TPU
[AI] 3-1. TensorFlow 介紹
TensorFlow 是一個開源的 Python 機器學習框架,主要由 Google 開發。 TensorFlow 可以做到以下的事: 可進行自動微分、計算梯度。 可在 CPU 上運行,也可以在 GPU 及 TPU 上運行。 可以將運算程序分散到多台機器上共同執行。 可以匯出為各種不同語言的程式,包含 C++, JavaScript 或 TensorFlow Lite,使得 TensorFlow 應用可以輕鬆部署到各種實際場景。
[AI] 2-3. 優化器 Optimizer
在神經網路中,每個神經層都通過以下的方式進行資料轉換 output = relu(dot(W, input) + b) W 和 b 為該層的屬性張量,統稱為該層的權重(weights) 或可訓練參數(trainable parameters),分別為 內核(kernel) 屬性和 偏值(bias) 屬性。 我們的目標是要透過訓練(training) 來進行權重的微調,進而得到最小的損失值。 參數調大或調小,可能會對應到「損失值變大」或「損失值變小」兩種可能(如果兩者皆變小,稱為 saddle point,兩者皆變大,可能為 local minium)。 但如果透過兩個方向的正向傳播來檢查微調的方向,效率很低,於是科學家引入了梯度下降法。 梯度下降法(gradient descent) 張量運算的導數稱為梯度(gradient)。 純量函數的導數相當於在函數曲線上找某一點的斜率。 張量函數上求梯度,相當於在函數描繪的多維曲面上求曲率。 y_pred = dot(W, x) loss_value = loss(y_pred, y_true) 假設只考慮 W 為變數,我們可以將 loss_value 視為 W 的函數。 loss_value = loss(dot(W, x), y_true) = f(W) 假設 W 的當前值為 W0,那麼函數 f 在 W0 這點的導數就是 $$ f’(W)=\frac{d\text{(loss\_value)}}{d\text{W}}|\text{W}_0 $$ 我們將上式寫成 g = grad(loss_value, W0) 代表了在 W0 附近,loss_value = f(W) 的梯度最陡方向之張量,我們可以透過往往「斜率反方向」移動來降低 f(W)。 ...
[AI] 2-2. 張量 Tensor
張量 Tensor 是神經網路的資料表示法。 在 Python,我們常用 NumPy 陣列來作為機器學習的基礎資料結構,說 NumPy 陣列也稱為張量。 張量的維、階、軸 階(rank): 又稱為軸(axis),代表陣列的軸數。 維(dimension): 某一階的元素個數。 1. 純量 (0D 張量) 純量是單一個數值,也稱為 0 維張量 (0D Tensor) x = 5 2. 向量 (1D 張量) 向量是包含單一軸的數列,也稱為 1 維張量 (1D Tensor) x = [1, 2, 3] 3. 矩陣 (2D 張量) 矩陣是二維的數據結構,也稱為 2 維張量 (2D Tensor) x = [[1, 2, 3], [4, 5, 6]] 4. 3D 張量與高階張量 3D 張量:在二維矩陣基礎上增加一個深度維度,常用於處理圖片數據 (例如 RGB 通道)。 高階張量 (nD 張量):當張量的維度超過 3D 時,用於更高維度的資料表示,例如影片、文字數據等。 x = [[[1, 2, 3], [4, 5, 6], [7, 8, 9]], [[10, 11, 12], [13, 14, 15], [16, 17, 18]]] 5. 張量的屬性 張量擁有幾個關鍵屬性,用於描述其結構與內容: ...
[AI] 2-1. 初試神經網路-手寫辨識 mnist
MNIST 是經典的手寫數字圖片資料集,已經內建在 tensorflow 裡面,這個資料集可以相當於是深度學習的 “Hello World”,是由美國國家標準與技術研究院(National Institute of Standard and Technology) 所提供。 1. 認識 MNIST 資料集 透過 tensorflow 引入資料集 from tensorflow.keras.datasets import mnist (train_images, train_labels), (test_images, test_labels) = mnist.load_data() 查看資料的大小 train_images.shape len(train_labels) test_images.shape test_labels min(test_labels), max(test_labels) 結果 (60000, 28, 28) # 代表 train_images 是 60000 張 28*28 的圖片 60000 # 代表 train_labels 同樣也有 60000 份 (10000, 28, 28) # 代表 test_images 有 10000 張 28*28 的圖片 array([7, 2, 1, ..., 4, 5, 6], dtype=uint8) (0, 9) # 代表 test_labels 是 0-9 的數字,資料型別是 uint8 2. 神經網路架構 接下來的操作流程是: 將測資 train_images 和 train_labels 餵給神經網路 神經網路學習分類圖片,與每張圖片的標籤對比,分類錯誤就修正(學習) 最後對 test_images 進行預測,並驗證結果看是否與 test_labels 吻合 from tensorflow import keras from tensorflow.keras import layers model = keras.Sequential([ layers.Dense(512, activation="relu") layers.Dense(10, activation="softmax") ]) 組成神經網路的基本元件為層(layer),一個層就是一個資料處理的模組。可以視之為資料的過濾器。具體而言,每一層都會從資料中萃取出特定的轉換或是表示法(representation),這些特定的表示法會有助於解決某些問題。大多數深度學習模型會將許多層連接在一起,漸次執行資料萃取(data distillation)。 在範例中,神經網路由兩個密集層(Dense layers)緊密連接組成,密集層也稱為全連接(fully connected) 神經層。第二個密集層是有 10 個輸出的 softmax 層,最終會輸出 10 個機率評分的陣列,每個評分就是對應到每一個數字的機率。 model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy']) 為了讓神經網路接受訓練,還需要準備三個元件才能進行編譯。 ...
[AI] 1-3. 深度學習的發展
卷積神經網路與反向傳播這兩個應用於電腦視覺的深度學習關鍵概念,在 1989 年就已經被研究透徹。長短期記憶(Long Short-Term Memory, LSTM) 演算法是時間序列的深度學習基礎,也於 1997 年發展出來,之後幾乎沒有什麼演進。那麼為什麼深度學習會在 2012 年後才開始蓬勃發展呢? 技術演變 硬體 資料集和競賽評比 演算法的進步 因為機器學習的發展是經由實驗結果來驗證(而不是由理論引導),所有只有當資料和硬體可支撐新思維時,才能促進演算法的進步。 硬體 從 1990 到 2010 年,CPU 提升了約 5000 倍,但這仍然無法供應電腦視覺或語音辨識的典型深度學習模型。 在 2000 年代,由 NVIDIA 和 AMD 等公司投資大規模平行運算晶片(graphical processing units, 圖形處理單元, GPU),以應愈來愈逼真的影音遊戲。在 2007 年,NVIDIA 推出了 CUDA,一個針對 GPU 的程式開發介面。從物理建模開始,只需要 GPU 就可以在各種高度平行化運算中取代大量 CPU。深度神經網路主要由許多矩陣多項式構成,也屬於高度平行化處理。因此在 2011 年,開始有研究人員(Dan Ciresan, Alex Krizhevsky)以 CUDA 來開發神經網路。 資料 除了過去 20 年儲存設備爆發式的發展外,網際網路的興起才是真正影響資料源的重大關鍵,大量來自於網路的資料被應用於機器學習上。 ImageNet 提供了大量的已被標註(labled)的影像,包含許多大尺寸的影像資料,還有最重要的,其相關的年度影像辨識競賽。 Kaggle 是一個專注於資料科學和機器學習的競賽平台,於 2010 年成立。這個平台不僅提供大量的公開資料集,還定期舉辦競賽,讓研究者和工程師能夠透過實際問題來測試與優化他們的演算法。Kaggle 競賽的主題多元,涵蓋醫學影像分析、自然語言處理、時間序列預測等。透過這樣的競爭環境,不僅促進了演算法的進步,也加速了實驗結果的分享與技術的迭代。 ImageNet 與 Kaggle 的共同點在於,它們都提供了一個標準化的評測基準,使得研究人員能夠客觀地比較不同的模型表現。此外,這些平台的出現,使得深度學習的研究與應用從少數實驗室走向全球,更多人參與的結果是加速了技術的發展。 演算法的進步 深度學習模型的核心挑戰之一在於深層網路結構中的梯度傳播問題(gradient propagation)。隨著網路層數的增加,用於訓練神經網路的回饋訊號可能會逐漸消失。這個問題在早期深度學習的研究中,極大地限制了神經網路的深度及其表現能力。然而,隨著多項關鍵演算法改進的提出,這一瓶頸逐漸被克服,深層網路得以成功訓練並展現強大的性能。 ...
[AI] 1-2. 機器學習的基礎技術
機率建模(Probabilistic modeling) 單純貝氏演算法(Naive Bayes thorem) 單純貝氏定理是一種基於機率理論的分類方法,適用於文本分類等任務,其核心概念是基於特徵條件獨立的假設來計算後驗機率。 以下是一個使用 Python 使現文本分類的範例 from sklearn.naive_bayes import MultinomialNB from sklearn.feature_extraction.text import CountVectorizer # 示範文本數據 texts = ["這部電影很好看", "服務很差", "餐點美味", "環境很糟"] labels = ["正面", "負面", "正面", "負面"] # 將文本轉換為特徵向量 vectorizer = CountVectorizer() X = vectorizer.fit_transform(texts) # 訓練模型 clf = MultinomialNB() clf.fit(X, labels) # 預測新文本 new_text = ["這家餐廳很棒"] new_X = vectorizer.transform(new_text) prediction = clf.predict(new_X) 邏輯迴歸(logistic regression, logreg) 邏輯迴歸是一種二元分類問題的基礎演算法。儘管名稱中有「迴歸」,但實際上是一個分類模型,通過 sigmoid 函數 將線性預測轉換成機率值。 from sklearn.linear_model import LogisticRegression import numpy as np # 示範數據 X = np.array([[1, 2], [2, 3], [3, 4], [4, 5]]) y = np.array([0, 0, 1, 1]) # 創建和訓練模型 model = LogisticRegression() model.fit(X, y) # 預測 prediction = model.predict([[2.5, 3.5]]) 早期的神經網路 1980年代的神經網路發展奠定了現代深度學習的基礎。反向傳播算法的發明是一個重要突破,它提供了一種有效的方法來訓練多層神經網路。 ...