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