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