人工智能教程(七):Scikit-learn 和訓練第一個模型
在本系列的 上一篇文章 中,我們用 TensorFlow 構建了第一個神經網絡,然后還通過 Keras 接觸了第一個數據集。我們還將介紹另一個強大的機器學習 Python 庫 scikit-learn。不過在進入正題之前,我要介紹兩個轟動性的人工智能應用:ChatGPT 和 DALL-E 2。(LCTT 譯注:此文原文發表于 2023 年初,恰值以 ChatGPT 為代表的 AI 熱潮開始掀起。)
OpenAI 是一個人工智能研究實驗室,它在人工智能和機器學習領域做了很多研究。埃隆·馬斯克Elon Musk 是該組織的聯合創始人之一。2022 年 11 月,該實驗室推出了一款名為 ChatGPT 的在線工具。它是一個可以像人類一樣聊天的人工智能聊天機器人。它是使用監督學習和強化學習技術訓練的大型語言模型large language model(LLM)。ChatGPT 使用了 OpenAI 的 GPT-3.5 語言模型,這是 GPT-3(生成式預訓練變換器Generative Pre-trained Transformer)的改進版本,GPT-3 是一種使用深度學習來生成類似人類文本的語言模型。(LCTT 譯注:OpenAI 已于 2023 年 3 月 14 日 發布了 GPT-4.0,它支持圖文混合的輸入輸出,并大幅提升了推理能力和準確性。)我仍然記得第一次使用 ChatGPT 時的興奮。它清楚地展現了人工智能的能力。ChatGPT 的回答質量很高,通常與人類給出的答案沒有區別。你可以使用它來糾正語法錯誤、改寫句子、總結段落、編寫程序等。實際上,我就用 ChatGPT 改寫了本文中的許多句子。此外,我還故意使用有語法錯誤的文本測試了 ChatGPT,它糾正后的句子非常準確。它重新措辭和總結段落的能力也很驚人。
程序員甚至有可能使用 ChatGPT 在短時間內解決編程難題。在 編程探險挑戰賽 2022Advent of Code 2022 中,就有人這樣宣稱(LCTT 譯注:比賽官方只是沒有完全禁止使用人工智能作為輔助,但是并不很推崇這樣的作法。消息來源)。事實上在 2022 年 12 月,也就是 ChatGPT 發布的一個月后,Stack Overflow 發布了一條新的規定,禁止提交 GPT 或 ChatGPT 生成答案。(LCTT 譯注:消息來源:Temporary policy Generative AI (e.g., ChatGPT) is banned - Meta Stack Overflow)
圖 1:ChatGPT 生成的程序
圖 1 顯示了 ChatGPT 編寫的將兩個矩陣相加的 Python 程序。我要求用 BASIC、FORTRAN、Pascal、Haskell、Lua、Pawn、C、c++、Java 等語言編寫程序,ChatGPT 總能給出答案,甚至對于像 Brainfuck 和 Ook! 這樣生僻的編程語言也是如此。我很確定 ChatGPT 沒有從互聯網上復制程序代碼。更確切地說,我認為 ChatGPT 是基于對上述編程語言的語法知識生成了這些答案的,這些知識是從訓練它的大量數據中獲得的。許多專家和觀察人士認為,隨著 ChatGPT 的發展,人工智能已經成為主流。ChatGPT 的真正力量將在未來幾個月或幾年里被看到。
OpenAI 的另一個令人驚嘆的在線人工智能工具是 DALL-E 2,它以卡通機器人 WALL-E(LCTT 譯注:電源《機器人總動員》中的主角)和著名畫家/藝術家 薩爾瓦多·達利Salvador Dalí
圖 2: DALL-E 2 生成的立體主義畫作
介紹 scikit-learn
scikit-learn 是一個非常強大的機器學習 Python 庫。它是一個采用 新 BSD 許可協議new BSD licence(LCTT 譯注:即三句版 BSD 許可證) 的自由開源軟件。scikit-learn 提供了回歸、分類、聚類和降維等當面的算法,如支持向量機Support Vector Machine(SVM)、隨機森林、k-means 聚類等。
在下面關于 scikit-learn 的介紹中,我們將通過代碼討論支持向量機。支持向量機是機器學習中的一個重要的監督學習模型,可以用于分類和回歸分析。支持向量機的發明人 Vladimir Vapnik 和 Alexey Chervonenkis。他們還一起提出了 VC 維Vapnik–Chervonenkis dimension
圖 3 是使用支持向量機對數據進行分類的程序。第 1 行從 scikit-learn 導入 svm 模塊。跟前面幾篇中介紹的 python
庫一樣,scikit-learn 也可以通過 Anaconda Navigator 輕松安裝。第 2 行定義了一個名為 X
的列表,其中包含訓練數據。X
中的所有元素都是大小為 3 的列表。第 3 行定義了一個列表 y
,其中包含列表 X
中數據的類別標簽。在本例中,數據屬于兩個類別,標簽只有 0 和 1 兩種。但是使用該技術可以對多個類別的數據進行分類。第 4 行使用 svm 模塊的 SVC()
方法生成一個支持向量分類器。第 5 行使用 svm 模塊的 fit()
方法,根據給定的訓練數據(本例中為數組 X
和 y
)擬合 svm 分類器模型。最后,第 6 行和第 7 行基于該分類器進行預測。預測的結果也顯示在圖 3 中。可以看到,分類器能夠正確區分我們提供的測試數據。
圖 3: 使用 SVM 進行分類
圖 4 中的代碼是一個使用 SVM 進行回歸的例子。第 1 行次從 scikit-learn 導入 svm 模塊。第 2 行定義了一個名為 X
的列表,其中包含訓練數據。注意,X
中的所有元素都是大小為 2 的列表。第 3 行定義了一個列表 y
,其中包含與列表 X
中的數據相關的值。第 4 行使用 svm 模塊的 SVR()
方法生成支持向量回歸模型。第 5 行使用 svm 模塊的 fit()
方法,根據給定的訓練數據(本例中為數 X
和 y
)擬合 svm 回歸模型。最后,第 6 行根據該 svm 回歸模型進行預測。此預測的結果顯示在圖 4 中。除了 SVR()
之外,還有 LinearSVR()
和 NuSVR()
兩種支持向量回歸模型。將第 4 行替換為 regr = svm.LinearSVR()
和 regr = svm.NuSVR()
,并執行代碼來查看這些支持向量回歸模型的效果。
圖 4:使用 SVM 進行回歸
現在讓我們把注意力轉到神經網絡和 TensorFlow 上。但在下一篇講無監督學習和聚類時,我們還會學習 scikit-learn 提供的其他方法。
神經網絡和 TensorFlow
在上一篇中我們已經看到了 TensorFlow 的 nn 模塊提供的 ReLU (整流線性單元rectified linear unit)和 Leaky ReLU 兩個激活函數,下面再介紹兩個其他激活函數。tf.nn.crelu()
是串聯 ReLU 激活函數。tf.nn.elu()
是 指數線性單元exponential linear unit
在開始訓練模型之前,我想向你分享 TensorFlow 的提供的“神經網絡實驗場”工具。它通過可視化的方式幫助你理解神經網絡的工作原理。你可以直觀地向神經網絡中添加神經元和隱藏層,然后訓練該模型。你可以選擇 Tanh、Sigmoid、Linear 和 ReLU 等激活函數。分類模型和回歸模型都可以使用該工具進行分析。訓練的效果以動畫的形式顯示。圖 5 顯示了一個示例神經網絡和它的輸出。你可以通過 https://playground.tensorflow.org 訪問它。
圖 5:神經網絡實驗場
訓練第一個模型
現在,我們使用 上一篇 提到的 MNIST 手寫數字數據集來訓練模型,然后使用手寫數字圖像對其進行測試。完整的程序 digital.py
相對較大,為了便于理解,我將程序拆分成幾個部分來解釋,并且添加了額外的行號。
import numpy as np
from tensorflow import keras, expand_dims
from tensorflow.keras import layers
num_classes = 10
input_shape = (28, 28, 1)
(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data( )
第 1 行到第 3 行加載必要的包和模塊。第 4 行將類別的數量定義為 10,因為我們試圖對 0 到 9 進行分類。第 5 行將輸入維度定義為 (28,28,1)
,這表明我們使用是 28 x 28 像素的灰度圖像數據。第 6 行加載該數據集,并將其分為訓練數據和測試數據。關于該數據集的更多信息可以參考 上一篇 的相關介紹。
x_train = x_train.astype("float32") / 255
x_test = x_test.astype("float32") / 255
x_train = np.expand_dims(x_train, 3)
x_test = np.expand_dims(x_test, 3)
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
第 7 行和第 8 行將圖像像素值從 [0,255]
轉換到 [0,1]
。其中 astype()
方法用于將整數值類型轉換為浮點值。第 9 行和第 10 行將數組 x_test
和 x_train
的維度從 (60000,28,28)
擴展為 (60000,28,28,1)
。列表 y_train
和 y_test
包含從 0 到 9 的 10 個數字的標簽。第 11 行和第 12 行將列表 y_train
和 y_test
轉換為二進制類別矩陣。
try:
model = keras.models.load_model(“existing_model”)
except IOError:
model = keras.Sequential(
[
keras.Input(shape=input_shape),
layers.Conv2D(32, kernel_size=(3, 3), activation=”relu”),
layers.MaxPooling2D(pool_size=(2, 2)),
layers.Conv2D(64, kernel_size=(3, 3), activation=”relu”),
layers.MaxPooling2D(pool_size=(2, 2)),
layers.Flatten( ),
layers.Dropout(0.5),
layers.Dense(num_classes, activation=”softmax”),
]
)
batch_size = 64
epochs = 25
model.compile(loss=”categorical_crossentropy”, optimizer=”adam”, metrics=[“accuracy”])
model.fit(x_train, y_train, batch_size=batch_size, epochs=epochs, validation_split=0.1)
model.save(“existing_model”)
訓練模型是一個處理器密集和高內存消耗的操作,我們可不希望每次運行程序時都要重新訓練一遍模型。因此,在第 13 行和第 14 行中,我們先嘗試從 existing_model
目錄加載模型。第一次執行此代碼時,沒有模型存在,因此會引發異常。第
16 到 21 行通過定義、訓練和保存模型來處理這個異常。第 16
行代碼(跨越多行)定義了模型的結構。這一行的參數決定了模型的行為。我們使用的是一個序列模型,它有一系列順序連接的層,每一層都有一個輸入張量和一個輸出張量。我們將在下一篇文章中討論這些定義模型的參數。在此之前,將這個神經網絡看作一個黑箱就可以了。
第 17 行將批大小定義為 64,它決定每批計算的樣本數量。第 18 行將 epoch 設置為
25,它決定了整個數據集將被學習算法處理的次數。第 19 行對模型的訓練行為進行了配置。第 20
行根據給定的數據和參數訓練模型。對這兩行代碼的詳細解釋將推遲到下一篇文章中。最后,第 21 行將訓練好的模型保存到 existing_model
目錄中。模型會以多個 .pb
文件的形式保存在該目錄中。注意,第 16 到 21 行位于 except
塊中。
print(model.summary( ))
score = model.evaluate(x_test, y_test, verbose=0)
print(“Test loss:”, score[0])
print(“Test accuracy:”, score[1])
第 22 行打印我們訓練的模型的摘要信息(見圖 6)?;叵胍幌?,在加載數據集時將其分為了訓練數據和測試數據。第 23 行使用測試數據來測試我們訓練的模型的準確性。第 24 行和第 25 行打印測試的詳細信息(見圖 8)。
圖 6:模型的細節信息
圖 6:模型的細節信息
img = keras.utils.load_img("sample1.png").resize((28, 28)).convert('L')
img = keras.utils.img_to_array(img)
img = img.reshape((1, 28, 28, 1))
img = img.astype('float32')/255
score = model.predict(img)
print(score)
print("Number is", np.argmax(score))
print("Accuracy", np.max(score) * 100.0)
現在,是時候用實際數據來測試我們訓練的模型了。我在紙上寫了幾個數字,并掃描了它們。圖 7 是我用來測試模型的一個圖像。第 26
行加載圖像,然后將其大小調整為 28 x 28 像素,最后將其轉換為灰度圖像。第 27 到 29
行對圖像進行必要的預處理,以便將它輸入到我們訓練好的模型中。第 30 行預測圖像所屬的類別。第 31 到 33 行打印該預測的詳細信息。圖 8
顯示了程序 digital.py
的這部分輸出。從圖中可以看出,雖然圖像被正確識別為 7,但置信度只有
23.77%。進一步,從圖 8 中可以看到它被識別為 1 的置信度為 12.86%,被識別為 8 或 9 的置信度約為
11%。此外,該模型甚至在某些情況下會是分類錯誤。雖然我找不到導致性能低于標準的準確原因,但我認為相對較低的訓練圖像分辨率以及測試圖像的質量可能是主要的影響因素。這雖然不是最好的模型,但我們現在有了第一個基于人工智能和機器學習原理的訓練模型。希望在本系列的后續文章中,我們能構建出可以處理更困難任務的模型。
圖 7:測試手寫數字樣例
在本文介紹了 scikit-learn,在下一篇文章中我們還會繼續用到它。然后介紹了一些加深對神經網絡的理解的知識和工具。我們還使用 Keras 訓練了第一個模型,并用這個模型進行預測。下一篇文章將繼續探索神經網絡和模型訓練。我們還將了解 PyTorch,這是一個基于 Torch 庫的機器學習框架。PyTorch 可以用于開發 計算機視覺computer vision(CV) 和 自然語言處理natural language processing(NLP) 相關的應用程序。
圖 8:digit.py 腳本的輸出
致謝:感謝我的學生 Sreyas S. 在撰寫本文過程中提出的創造性建議。