教程:從環(huán)境搭建到模型評(píng)估的機(jī)器學(xué)習(xí)全流程)
最近身邊不少朋友開(kāi)始學(xué)機(jī)器學(xué)習(xí)第一個(gè)問(wèn)題基本都是“我從哪個(gè)庫(kù)入手”。我的回答永遠(yuǎn)一致先別碰框架老老實(shí)實(shí)把Scikit-learn學(xué)明白。這個(gè)庫(kù)在機(jī)器學(xué)習(xí)生態(tài)里的地位相當(dāng)于螺絲刀在工具箱里的地位干不了所有活但幾乎所有活都離不開(kāi)它。尤其對(duì)新手來(lái)說(shuō)Scikit-learn能幫你把分類、回歸、聚類、降維、數(shù)據(jù)預(yù)處理這些機(jī)器學(xué)習(xí)核心流程全部跑通而且API設(shè)計(jì)極其統(tǒng)一學(xué)一個(gè)模型等于會(huì)了十幾個(gè)模型。這篇教程我按自己帶新人踩坑的經(jīng)驗(yàn)來(lái)寫(xiě)目標(biāo)是讓一個(gè)完全沒(méi)接觸過(guò)機(jī)器學(xué)習(xí)、只有一點(diǎn)點(diǎn)Python基礎(chǔ)的人也能在半天內(nèi)跑通第一個(gè)項(xiàng)目并且明白每一步到底在做什么。文中的代碼我都會(huì)貼出可直接運(yùn)行的版本用的數(shù)據(jù)集也是Scikit-learn自帶的不需要額外下載任何數(shù)據(jù)文件。1. 先搞清楚Scikit-learn能做什么以及它憑什么成為入門(mén)首選1.1 它解決的到底是什么問(wèn)題很多人把“機(jī)器學(xué)習(xí)”想象得很玄其實(shí)剝開(kāi)看就四件事準(zhǔn)備數(shù)據(jù)、訓(xùn)練模型、評(píng)估效果、用模型做預(yù)測(cè)。Scikit-learn的作用就是把這四個(gè)環(huán)節(jié)的工具全部打包好并且接口風(fēng)格高度一致。舉個(gè)例子你想讓電腦學(xué)會(huì)“認(rèn)識(shí)貓”。本質(zhì)上你要做的是收集一堆標(biāo)注好的圖片特征比如毛發(fā)顏色、耳朵形狀、體型大小把這些特征整理成表格然后讓算法從表格中總結(jié)出一套規(guī)律——什么樣的特征組合更像貓。有了規(guī)律之后來(lái)一張新圖片提取出同樣的特征喂給這套規(guī)律模型輸出“貓”或“不是貓”。Scikit-learn就負(fù)責(zé)“讓算法總結(jié)規(guī)律”和“用規(guī)律做預(yù)測(cè)”這兩件事前期的數(shù)據(jù)收集和特征整理通常配合pandas和numpy來(lái)做。它覆蓋的算法非常全線性回歸、邏輯回歸、決策樹(shù)、隨機(jī)森林、支持向量機(jī)、K近鄰、K均值聚類、主成分分析等等只要是傳統(tǒng)機(jī)器學(xué)習(xí)范疇的經(jīng)典算法基本都實(shí)現(xiàn)了。深度學(xué)習(xí)相關(guān)的內(nèi)容不在它的范圍內(nèi)那是PyTorch和TensorFlow的主場(chǎng)。1.2 為什么新手第一站就選它我見(jiàn)過(guò)很多人一上來(lái)就學(xué)TensorFlow結(jié)果被計(jì)算圖、張量、GPU配置這些概念勸退。Scikit-learn最大的優(yōu)勢(shì)就是統(tǒng)一API——所有模型都長(zhǎng)一個(gè)樣都要經(jīng)歷fit、predict、score這三個(gè)階段。這意味著你今天學(xué)會(huì)了用K近鄰做分類明天換成隨機(jī)森林只需要改一行引入模型的代碼后面幾乎不用動(dòng)。它的官方文檔也是我在所有開(kāi)源庫(kù)里見(jiàn)過(guò)最友好的每個(gè)算法都有完整案例每一步都能看懂。相比讀論文或者啃源碼新手用Scikit-learn學(xué)到的不是某個(gè)算法的數(shù)學(xué)推導(dǎo)而是機(jī)器學(xué)習(xí)的完整工作流。這個(gè)工作流思維恰恰是后面學(xué)任何框架都通用的底層能力。另外Scikit-learn的代碼風(fēng)格非常Pythonic底層用Cython做了大量?jī)?yōu)化訓(xùn)練小規(guī)模數(shù)據(jù)的速度相當(dāng)快。你在筆記本上跑一個(gè)隨機(jī)森林可能幾秒鐘就出結(jié)果這種即時(shí)反饋對(duì)新手建立信心特別重要。2. 環(huán)境準(zhǔn)備從零搭建一套能跑通的Python機(jī)器學(xué)習(xí)環(huán)境2.1 用Anaconda還是純pip我推薦后者關(guān)于安裝方式網(wǎng)上一搜全是“裝Anaconda一鍵搞定”。Anaconda確實(shí)省事但它自帶的Python環(huán)境和包版本往往比較舊而且整包體積好幾個(gè)G里面大部分工具你根本用不到。我自己現(xiàn)在的做法是直接用系統(tǒng)Python加上venv虛擬環(huán)境再用pip裝包。這種方式更干凈排查問(wèn)題也更容易。如果你是在Windows上我建議裝一個(gè)穩(wěn)定版Python3.9到3.12之間都行別追最新版有些庫(kù)的預(yù)編譯包還沒(méi)跟上安裝時(shí)記得勾選“Add Python to PATH”。然后在終端里執(zhí)行mkdir ml_project cd ml_project python -m venv venvWindows下激活虛擬環(huán)境venv\Scripts\activatemacOS或Linux下激活source venv/bin/activate看到命令行前面出現(xiàn)(venv)字樣說(shuō)明虛擬環(huán)境已經(jīng)激活接下來(lái)裝的包都只在這個(gè)項(xiàng)目里生效不會(huì)污染全局環(huán)境。2.2 快速安裝并驗(yàn)證5分鐘確認(rèn)全家桶是否工作在激活的虛擬環(huán)境里直接一條命令裝齊入門(mén)四件套pip install numpy pandas matplotlib scikit-learn裝完千萬(wàn)別急著關(guān)命令行先做一次驗(yàn)證確認(rèn)所有包都能正常導(dǎo)入并且版本兼容python -c import numpy, pandas, matplotlib, sklearn; print(numpy.__version__); print(pandas.__version__); print(matplotlib.__version__); print(sklearn.__version__)這四個(gè)包的關(guān)系我習(xí)慣這么理解numpy管多維數(shù)組pandas管表格數(shù)據(jù)matplotlib管可視化scikit-learn管建模。前三個(gè)可以看成是給Scikit-learn打雜的一個(gè)機(jī)器學(xué)習(xí)項(xiàng)目里基本離不開(kāi)它們。再跑一段最簡(jiǎn)單的測(cè)試代碼加載Scikit-learn自帶的鳶尾花數(shù)據(jù)集確認(rèn)數(shù)據(jù)能讀出來(lái)from sklearn.datasets import load_iris iris load_iris() print(iris.data.shape) print(iris.target_names) print(iris.feature_names)如果輸出結(jié)果是(150, 4)和[setosa versicolor virginica]以及四個(gè)特征名說(shuō)明環(huán)境完全沒(méi)問(wèn)題可以開(kāi)始寫(xiě)真正的項(xiàng)目了。注意常見(jiàn)的一個(gè)坑是Python版本太高導(dǎo)致Scikit-learn安裝失敗。如果pip install報(bào)錯(cuò)優(yōu)先檢查Python版本是否為3.12或以下再檢查是否有網(wǎng)絡(luò)代理干擾。實(shí)在不行換成Python 3.10這個(gè)版本兼容性最好。3. 第一個(gè)完整項(xiàng)目用鳶尾花數(shù)據(jù)走通機(jī)器學(xué)習(xí)全流程3.1 加載數(shù)據(jù)別急著寫(xiě)模型先認(rèn)識(shí)你的數(shù)據(jù)鳶尾花數(shù)據(jù)集是機(jī)器學(xué)習(xí)界的Hello World包含150條樣本每條有4個(gè)特征花瓣長(zhǎng)度、花瓣寬度、花萼長(zhǎng)度、花萼寬度標(biāo)簽是三種鳶尾花品種。這個(gè)數(shù)據(jù)集干凈、量小、分類界限清晰非常適合新手跑通流程。先加載并觀察數(shù)據(jù)的形式from sklearn.datasets import load_iris import pandas as pd iris load_iris() df pd.DataFrame(iris.data, columnsiris.feature_names) df[target] iris.target print(df.head()) print(df[target].value_counts())為什么先把數(shù)據(jù)轉(zhuǎn)成DataFrame因?yàn)閜andas的表格形式更接近真實(shí)世界的數(shù)據(jù)形態(tài)你能直觀看到每一列的含義和取值分布。真實(shí)項(xiàng)目中數(shù)據(jù)亂七八糟CSV、Excel、數(shù)據(jù)庫(kù)導(dǎo)出的都有第一步永遠(yuǎn)是“看懂?dāng)?shù)據(jù)長(zhǎng)什么樣”而不是馬上敲模型代碼。我觀察數(shù)據(jù)時(shí)一般問(wèn)三個(gè)問(wèn)題有幾行幾列、每列是數(shù)值還是類別、目標(biāo)列有多少種取值、分布是否均衡。這三個(gè)問(wèn)題直接決定后面選什么模型、需不需要做數(shù)據(jù)增強(qiáng)或類別平衡處理。3.2 數(shù)據(jù)拆分訓(xùn)練集和測(cè)試集不能亂切模型訓(xùn)練前必須把數(shù)據(jù)拆成訓(xùn)練集和測(cè)試集。訓(xùn)練集用來(lái)讓模型學(xué)習(xí)規(guī)律測(cè)試集用來(lái)檢驗(yàn)?zāi)P蛯W(xué)得怎么樣。如果拿同一份數(shù)據(jù)又訓(xùn)練又測(cè)試就相當(dāng)于考試前把答案給考生看了一眼分?jǐn)?shù)再高也沒(méi)有意義。Scikit-learn的train_test_split就是干這個(gè)的from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test train_test_split( iris.data, iris.target, test_size0.2, random_state42, stratifyiris.target )這里test_size0.2表示留出20%的數(shù)據(jù)做測(cè)試stratifyiris.target表示按標(biāo)簽比例分層抽樣保證訓(xùn)練集和測(cè)試集里三種花的比例跟原始數(shù)據(jù)一致。random_state42是個(gè)隨便寫(xiě)的整數(shù)作用是固定隨機(jī)數(shù)生成過(guò)程這樣你每次運(yùn)行代碼得到的拆分結(jié)果都一樣方便復(fù)現(xiàn)實(shí)驗(yàn)結(jié)果。注意stratify這個(gè)參數(shù)很多人會(huì)漏掉在分類任務(wù)里尤其重要。假設(shè)原始數(shù)據(jù)里貓占90%、狗占10%如果不分層隨機(jī)拆分可能測(cè)試集里全是貓模型訓(xùn)練時(shí)沒(méi)見(jiàn)過(guò)幾條狗的數(shù)據(jù)測(cè)試時(shí)遇到狗自然全錯(cuò)。這個(gè)坑我在實(shí)際項(xiàng)目中踩過(guò)不止一次。3.3 訓(xùn)練模型三行代碼跑出一個(gè)基線結(jié)果數(shù)據(jù)準(zhǔn)備好之后訓(xùn)練一個(gè)K近鄰分類器只需要三步from sklearn.neighbors import KNeighborsClassifier model KNeighborsClassifier(n_neighbors3) model.fit(X_train, y_train) print(model.score(X_test, y_test))就這三行一個(gè)能用的分類器已經(jīng)誕生了。score方法內(nèi)部會(huì)自動(dòng)做預(yù)測(cè)并與真實(shí)標(biāo)簽比對(duì)返回準(zhǔn)確率。在鳶尾花數(shù)據(jù)集上通常能得到0.93到1.0的準(zhǔn)確率。K近鄰的原理很直白一個(gè)新樣本進(jìn)來(lái)找到訓(xùn)練集里離它最近的K個(gè)樣本這K個(gè)樣本中哪個(gè)類別最多就預(yù)測(cè)成哪一類。n_neighbors3就是只看最近的3個(gè)鄰居。你可以試試把這個(gè)參數(shù)改成1、5、10觀察準(zhǔn)確率怎么變這是感受模型復(fù)雜度與效果關(guān)系最直觀的一個(gè)實(shí)驗(yàn)。跑完你會(huì)注意到我們完全沒(méi)做標(biāo)準(zhǔn)化卻也能得到高分。這是因?yàn)轼S尾花數(shù)據(jù)本身四個(gè)特征的量級(jí)差不多但真實(shí)數(shù)據(jù)里如果身高的數(shù)值是厘米、體重是千克、年齡是歲這三個(gè)特征直接拼在一起算距離身高就會(huì)主導(dǎo)結(jié)果模型基本學(xué)不到其他特征的信息。這個(gè)問(wèn)題的解法下一部分會(huì)詳細(xì)說(shuō)。4. 核心概念拆解fit、predict、transform到底在干嘛4.1 fit是什么從“認(rèn)貓”這個(gè)類比說(shuō)開(kāi)去Scikit-learn里面所有的模型和數(shù)據(jù)轉(zhuǎn)換器都有fit方法。fit的中文意思接近“擬合”你可以把它理解成“讓模型學(xué)習(xí)訓(xùn)練數(shù)據(jù)里的規(guī)律”的動(dòng)作。我經(jīng)常用“認(rèn)識(shí)貓”的例子給學(xué)生講你小時(shí)候看了一百?gòu)堌埖膱D片腦子里慢慢形成了對(duì)貓的判斷標(biāo)準(zhǔn)這個(gè)過(guò)程就是fit。訓(xùn)練好的模型里存下來(lái)的不是那些圖片本身而是從圖片特征里提取出來(lái)的規(guī)律參數(shù)。K近鄰稍微特殊一點(diǎn)它沒(méi)有顯式的參數(shù)fit之后只是把訓(xùn)練數(shù)據(jù)原封不動(dòng)存起來(lái)真正計(jì)算發(fā)生在predict的時(shí)候所以它被叫做“惰性學(xué)習(xí)”算法。data_transform也一樣比如StandardScaler的fit是在計(jì)算訓(xùn)練集每一列的均值和標(biāo)準(zhǔn)差并把這兩個(gè)數(shù)值存在scaler對(duì)象里。注意計(jì)算出的均值和標(biāo)準(zhǔn)差只來(lái)自訓(xùn)練集測(cè)試集和未來(lái)的新數(shù)據(jù)都沿用這套參數(shù)去做標(biāo)準(zhǔn)化不能重新計(jì)算。這是防止數(shù)據(jù)泄露的關(guān)鍵。4.2 predict和score模型出來(lái)之后怎么用fit完成之后模型進(jìn)入可用狀態(tài)。predict就是喂新數(shù)據(jù)、拿預(yù)測(cè)結(jié)果predictions model.predict(X_test) print(predictions[:10]) print(y_test[:10])用predictions和y_test做對(duì)比就能人工檢查模型預(yù)測(cè)得對(duì)不對(duì)。score則是一個(gè)快捷方法直接返回準(zhǔn)確率內(nèi)部相當(dāng)于先predict再與真實(shí)標(biāo)簽比對(duì)算平均值。如果你用的是邏輯回歸這樣的概率模型還能用predict_proba拿到每個(gè)類別的概率值。這個(gè)接口在真實(shí)業(yè)務(wù)里特別常用比如銀行風(fēng)控系統(tǒng)最后不是簡(jiǎn)單輸出“欺詐”或“正?!倍禽敵鲆粋€(gè)欺詐概率再根據(jù)概率閾值決定是否觸發(fā)人工審核。這也是為什么邏輯回歸常被稱為實(shí)時(shí)評(píng)分系統(tǒng)的“主引擎”之一。4.3 為什么明明代碼能跑準(zhǔn)確率卻不行很多人跑到這一步會(huì)來(lái)問(wèn)我老師我代碼跟你的完全一樣為什么準(zhǔn)確率只有0.3這種情況我碰到太多次了十有八九是下面幾種原因第一沒(méi)有隨機(jī)種子且數(shù)據(jù)量很小。train_test_split默認(rèn)是隨機(jī)拆分的對(duì)150條這種小數(shù)據(jù)不同拆分方式可能導(dǎo)致結(jié)果波動(dòng)很大。解決辦法就是在train_test_split和模型中都固定random_state。第二分類目標(biāo)是字符串而不是數(shù)字。Scikit-learn要求y是整數(shù)編碼如果你直接塞一列“cat”“dog”進(jìn)去很多模型會(huì)報(bào)錯(cuò)或者把字符串當(dāng)成類別編號(hào)。解決辦法是用LabelEncoder做一次編碼。第三模型沒(méi)fit或者fit的是數(shù)據(jù)轉(zhuǎn)換器而不是模型。我見(jiàn)過(guò)有人把StandardScaler當(dāng)模型來(lái)fit然后拿score去評(píng)估結(jié)果肯定是錯(cuò)的因?yàn)閟caler根本沒(méi)有score方法。這些問(wèn)題的共性就是沒(méi)有理解fit的對(duì)象是誰(shuí)、訓(xùn)練數(shù)據(jù)和測(cè)試數(shù)據(jù)有沒(méi)有混用。所以我一直強(qiáng)調(diào)學(xué)Scikit-learn前期不用背API先把自己做過(guò)的每組代碼里“誰(shuí)在fit、誰(shuí)在transform、誰(shuí)在predict”搞清楚比什么都重要。5. 數(shù)據(jù)預(yù)處理與特征工程新手最該花時(shí)間的地方5.1 缺失值處理不是刪掉就完事真實(shí)數(shù)據(jù)集基本沒(méi)有perfect的缺失值處理是第一道坎。最省事的方法是整行刪除但只要數(shù)據(jù)量不大刪一行可能就丟掉了寶貴的信息。更穩(wěn)的做法是填充Scikit-learn提供了SimpleImputerfrom sklearn.impute import SimpleImputer import numpy as np imputer SimpleImputer(strategymedian) X_filled imputer.fit_transform(X_with_missing)strategy可以選mean、median、most_frequent、constant。我一般優(yōu)先用median而不是mean因?yàn)閙ean對(duì)異常值敏感一個(gè)極端值就能把平均值拉得很遠(yuǎn)median則穩(wěn)健得多。要記住的是imputer只能拿訓(xùn)練集的數(shù)據(jù)去fit然后transform訓(xùn)練集和測(cè)試集。如果直接用全量數(shù)據(jù)fit后再拆分測(cè)試集的信息已經(jīng)滲透到填充值里了評(píng)估結(jié)果會(huì)虛高這屬于典型的數(shù)據(jù)泄露。5.2 標(biāo)準(zhǔn)化與歸一化影響巨大但極易忽略標(biāo)準(zhǔn)化和歸一化是特征縮放的兩類做法。標(biāo)準(zhǔn)化StandardScaler把數(shù)據(jù)變成均值為0、標(biāo)準(zhǔn)差為1歸一化MinMaxScaler把數(shù)據(jù)縮放到0到1之間。新手最容易犯的錯(cuò)誤是拿到數(shù)據(jù)直接建模完全不考慮特征量級(jí)問(wèn)題。K近鄰、支持向量機(jī)、邏輯回歸、主成分分析這些基于距離或梯度的算法對(duì)特征尺度非常敏感。比如房?jī)r(jià)預(yù)測(cè)里“面積”是80到200的數(shù)值“建造年份”是1900到2020的數(shù)值兩者直接算歐幾里得距離面積幾乎不起作用。下面這段代碼演示了標(biāo)準(zhǔn)化的標(biāo)準(zhǔn)用法from sklearn.preprocessing import StandardScaler scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test)注意測(cè)試集只能調(diào)用transform不能重新fit。scaler用訓(xùn)練集算出的均值和標(biāo)準(zhǔn)差已經(jīng)是全部信息測(cè)試集再說(shuō)自己是新來(lái)數(shù)據(jù)里的合法成員但規(guī)則要用老規(guī)矩。什么時(shí)候不需要標(biāo)準(zhǔn)化決策樹(shù)和隨機(jī)森林這類樹(shù)模型完全不受量綱影響因?yàn)樗鼈冏龅氖翘卣鞯那蟹峙袛喽皇怯?jì)算距離所以可以不做標(biāo)準(zhǔn)化。反過(guò)來(lái)線性回歸如果不做正則化特征量級(jí)差異大時(shí)正則化項(xiàng)會(huì)把小量級(jí)特征的特征系數(shù)壓得特別狠導(dǎo)致模型偏向大量級(jí)特征這時(shí)候標(biāo)準(zhǔn)化就很有必要。5.3 類別特征編碼LabelEncoder和OneHotEncoder的選擇很多原始數(shù)據(jù)里含有類別文字比如顏色“紅”“綠”“藍(lán)”城市“北京”“上?!薄皬V州”。這類數(shù)據(jù)不能直接喂給模型需要編碼成數(shù)字。新手最容易踩的坑是把全部分類變量統(tǒng)一用LabelEncoder編碼成0、1、2。問(wèn)題是LabelEncoder編碼出來(lái)的是有序整數(shù)模型會(huì)誤以為1比0大、2比1大這在不具有內(nèi)在順序的分類變量里會(huì)產(chǎn)生虛假的排序關(guān)系。比如顏色“紅”編碼成1“藍(lán)”編碼成2模型就會(huì)認(rèn)為“藍(lán)”在數(shù)值上比“紅”大這毫無(wú)意義。正確的做法通常是對(duì)無(wú)序類別特征用OneHotEncoder或者直接用pandas的get_dummiesimport pandas as pd df_encoded pd.get_dummies(df, columns[color], drop_firstTrue)OneHotEncoder把“紅”“綠”“藍(lán)”變成三個(gè)二值特征是不是紅、是不是綠、是不是藍(lán)。損失的是數(shù)據(jù)中原本不存在的順序信息換來(lái)的是模型不會(huì)誤用順序關(guān)系。對(duì)有序類別特征比如“低”“中”“高”可以用OrdinalEncoder或者手動(dòng)映射成0、1、2因?yàn)檫@時(shí)候順序確實(shí)有意義。實(shí)操心得真實(shí)項(xiàng)目里我強(qiáng)烈建議用Pipeline把標(biāo)準(zhǔn)化、編碼、建模串在一起。Pipeline的好處是能把數(shù)據(jù)預(yù)處理的參數(shù)和模型參數(shù)作為一個(gè)整體來(lái)調(diào)優(yōu)Cross-validation時(shí)也不會(huì)發(fā)生數(shù)據(jù)泄露因?yàn)槊恳徽鄱紩?huì)在訓(xùn)練子集上重新fit預(yù)處理步驟。新手可能一時(shí)體會(huì)不到但等你開(kāi)始用GridSearchCV調(diào)參時(shí)就會(huì)明白Pipeline有多省心。6. 模型選擇實(shí)用指南KNN、邏輯回歸與隨機(jī)森林6.1 KNN最適合入門(mén)的模型K近鄰是新手第一個(gè)模型因?yàn)樗鼛缀醪恍枰?xùn)練原理就是“物以類聚”。但KNN有兩個(gè)致命短板一是預(yù)測(cè)時(shí)需要計(jì)算新樣本與所有訓(xùn)練樣本的距離數(shù)據(jù)量大時(shí)非常慢二是它對(duì)特征縮放非常敏感不做標(biāo)準(zhǔn)化的KNN在真實(shí)數(shù)據(jù)上表現(xiàn)通常很差。實(shí)際使用中K值的選擇可以通過(guò)交叉驗(yàn)證來(lái)確定。K太小容易過(guò)擬合K太大模型過(guò)于平滑容易忽略局部規(guī)律。我自己在項(xiàng)目里一般從3、5、7、9這幾個(gè)值里挑配合交叉驗(yàn)證選最好的。KNN適合小規(guī)模數(shù)據(jù)、低維特征、類別邊界比較清晰的場(chǎng)景比如手寫(xiě)數(shù)字識(shí)別的小demo、個(gè)性化推薦的初始版本。大規(guī)模高維場(chǎng)景就別用KNN了不光慢距離在高維空間里也容易失效。6.2 邏輯回歸實(shí)時(shí)評(píng)分系統(tǒng)的主引擎很多人以為邏輯回歸只能做二分類其實(shí)它做多分類也行只是默認(rèn)的二分類場(chǎng)景用得最多。邏輯回歸輸出的不是類別而是屬于正類的概率這個(gè)特性讓它在金融風(fēng)控、廣告點(diǎn)擊率預(yù)估、推薦排序這些需要概率分?jǐn)?shù)的場(chǎng)景里成為主力。邏輯回歸的核心是在線性回歸基礎(chǔ)上套了一個(gè)Sigmoid函數(shù)把輸出壓縮到0到1之間。它的訓(xùn)練速度快、推理速度更快模型內(nèi)存占用小所以非常適合做實(shí)時(shí)評(píng)分系統(tǒng)。在CTR預(yù)估這類高維稀疏特征場(chǎng)景里即使深度學(xué)習(xí)大行其道的今天邏輯回歸依然有大量應(yīng)用。代碼同樣很簡(jiǎn)單from sklearn.linear_model import LogisticRegression logreg LogisticRegression(max_iter1000) logreg.fit(X_train, y_train) print(logreg.score(X_test, y_test)) print(logreg.predict_proba(X_test[:5]))一個(gè)我踩過(guò)的坑是忘記調(diào)max_iter邏輯回歸默認(rèn)迭代次數(shù)是100有些數(shù)據(jù)集上沒(méi)收斂就停了控制臺(tái)會(huì)彈出ConvergenceWarning雖然代碼沒(méi)報(bào)錯(cuò)但模型效果已經(jīng)不對(duì)了。訓(xùn)練前把max_iter設(shè)成1000或更高是最省事的做法。6.3 決策樹(shù)與隨機(jī)森林效果與可解釋性的平衡決策樹(shù)最大的優(yōu)點(diǎn)是透明可解釋。訓(xùn)練完可以把整棵樹(shù)畫(huà)出來(lái)每一步怎么判斷一目了然這在醫(yī)療、金融等需要向用戶解釋原因的行業(yè)特別重要。缺點(diǎn)是單棵決策樹(shù)容易過(guò)擬合稍微動(dòng)一點(diǎn)數(shù)據(jù)樹(shù)的結(jié)構(gòu)可能完全變樣。隨機(jī)森林就是批量訓(xùn)練一大堆決策樹(shù)再投票表決通過(guò)引入隨機(jī)性來(lái)降低方差效果比單棵樹(shù)穩(wěn)得多幾乎不調(diào)參也能得到不錯(cuò)的結(jié)果from sklearn.ensemble import RandomForestClassifier rf RandomForestClassifier(n_estimators100, random_state42) rf.fit(X_train, y_train) print(rf.score(X_test, y_test))n_estimators是樹(shù)的數(shù)量100是常用默認(rèn)值。隨機(jī)森林里有幾個(gè)重要的調(diào)參方向max_depth控制單棵樹(shù)的深度限制過(guò)擬合min_samples_leaf控制葉子節(jié)點(diǎn)的最少樣本數(shù)值越大模型越保守。新手階段不需要深究直接跑默認(rèn)效果已經(jīng)能碾壓很多模型。注意樹(shù)模型不需要特征標(biāo)準(zhǔn)化但它們對(duì)特征重要性有天然的“偏見(jiàn)”取值多的特征容易被選中。如果你發(fā)現(xiàn)隨機(jī)森林給出的特征重要性完全不符合業(yè)務(wù)直覺(jué)可以用permutation_importance做一次無(wú)偏的重要性評(píng)估這個(gè)函數(shù)在很多比賽里幫我發(fā)現(xiàn)了關(guān)鍵特征。7. 模型評(píng)估準(zhǔn)確率不是唯一指標(biāo)7.1 混淆矩陣比準(zhǔn)確率更誠(chéng)實(shí)準(zhǔn)確率是最常見(jiàn)的評(píng)估指標(biāo)但在類別不平衡的數(shù)據(jù)集上會(huì)騙人。舉個(gè)例子100個(gè)樣本里只有5個(gè)異常模型把所有樣本都預(yù)測(cè)成正常準(zhǔn)確率是95%看著很高但異常一個(gè)都沒(méi)抓出來(lái)。這種模型在風(fēng)控里約等于廢物。這時(shí)候要看的是一張混淆矩陣from sklearn.metrics import confusion_matrix, classification_report cm confusion_matrix(y_test, model.predict(X_test)) print(cm) print(classification_report(y_test, model.predict(X_test)))混淆矩陣的四個(gè)格子分別是真正例、假正例、真反例、假反例。從這個(gè)矩陣?yán)锬芩愠鼍_率precision和召回率recall。精確率是“預(yù)測(cè)為正類的樣本里有多少是真正類”召回率是“真實(shí)正類樣本里有多少被找了出來(lái)”。風(fēng)控場(chǎng)景更看重召回率因?yàn)槁┑粢粋€(gè)欺詐交易損失巨大推薦系統(tǒng)則更看重精確率因?yàn)橥扑]了10個(gè)內(nèi)容用戶一個(gè)都不點(diǎn)體驗(yàn)會(huì)很差。7.2 交叉驗(yàn)證別讓你的評(píng)估結(jié)果靠運(yùn)氣train_test_split把數(shù)據(jù)分一次就訓(xùn)練一次結(jié)果受隨機(jī)性影響很大。更穩(wěn)的做法是k折交叉驗(yàn)證把數(shù)據(jù)分成K份每次拿K-1份訓(xùn)練、1份驗(yàn)證輪流K次最后把K次結(jié)果取平均from sklearn.model_selection import cross_val_score scores cross_val_score(rf, iris.data, iris.target, cv5) print(scores) print(scores.mean())cv5是常用設(shè)置。交叉驗(yàn)證的價(jià)值在于你得到的是模型在5種不同數(shù)據(jù)劃分下的平均表現(xiàn)比單次劃分的結(jié)果可靠得多。GridSearchCV調(diào)參的原理就是配合交叉驗(yàn)證來(lái)做——在參數(shù)網(wǎng)格中嘗試每一組參數(shù)每組都跑K折交叉驗(yàn)證選出平均得分最高的一組。Newer用戶一定要建立這個(gè)習(xí)慣任何模型評(píng)估都不該依賴單次劃分的分?jǐn)?shù)交叉驗(yàn)證才是默認(rèn)選項(xiàng)。8. 新手最常踩的坑安裝、維度、過(guò)擬合一次說(shuō)清8.1 安裝報(bào)錯(cuò)怎么辦安裝Scikit-learn最常見(jiàn)的報(bào)錯(cuò)是pip install時(shí)找不到匹配版本或者編譯過(guò)程中各種依賴報(bào)錯(cuò)。先確認(rèn)Python版本Scikit-learn新版本一般只支持3.9以上過(guò)舊的版本可能裝不上。另一個(gè)經(jīng)常碰到的坑是電腦里裝了多個(gè)Python解釋器pip和python來(lái)自不同環(huán)境裝完導(dǎo)入?yún)s失敗。這種問(wèn)題推薦用python -m pip install來(lái)裝確保裝的包對(duì)應(yīng)當(dāng)前解釋器。8.2 維度不匹配報(bào)錯(cuò)很多新手遇到的報(bào)錯(cuò)長(zhǎng)這樣Expected 2D array, got 1D array instead。原因是模型要求輸入是二維數(shù)組行是樣本、列是特征你傳進(jìn)去的卻是一維數(shù)組。解決辦法是給數(shù)據(jù)增加一個(gè)維度import numpy as np single_sample np.array([[5.1, 3.5, 1.4, 0.2]]) model.predict(single_sample)這種情況尤其在只預(yù)測(cè)一條新樣本時(shí)最容易發(fā)生。另外用pandas處理完數(shù)據(jù)后要確認(rèn)X是DataFrame或二維NumPy數(shù)組別把Series傳進(jìn)去當(dāng)特征數(shù)據(jù)。8.3 模型過(guò)擬合或欠擬合怎么調(diào)過(guò)擬合的表現(xiàn)是訓(xùn)練集準(zhǔn)確率很高、測(cè)試集準(zhǔn)確率暴跌。常見(jiàn)原因有模型太復(fù)雜、特征太多、訓(xùn)練數(shù)據(jù)太少。解決辦法是正則化、剪枝、增大數(shù)據(jù)集、降低模型復(fù)雜度。以決策樹(shù)為例限制max_depth、提高min_samples_leaf都能有效抑制過(guò)擬合。欠擬合則是模型太簡(jiǎn)單訓(xùn)練集和測(cè)試集準(zhǔn)確率都不高。解決辦法反過(guò)來(lái)增加特征、換更強(qiáng)的模型、降低正則化強(qiáng)度。判斷是過(guò)擬合還是欠擬合有個(gè)簡(jiǎn)單經(jīng)驗(yàn)先看訓(xùn)練集得分如果訓(xùn)練集都學(xué)不好就是欠擬合訓(xùn)練集學(xué)得很好、測(cè)試集很差就是過(guò)擬合。8.4 常見(jiàn)問(wèn)題速查表問(wèn)題現(xiàn)象可能原因解決方案安裝時(shí)報(bào)紅字找不到包Python版本不匹配或網(wǎng)絡(luò)問(wèn)題換Python 3.10用鏡像源重試Expected 2D array報(bào)錯(cuò)特征維度不足用reshape調(diào)整數(shù)組形狀準(zhǔn)確率只有0.3左右隨機(jī)種子未固定、數(shù)據(jù)泄露固定random_state檢查預(yù)處理只用訓(xùn)練集fit訓(xùn)練集分?jǐn)?shù)高測(cè)試集分?jǐn)?shù)低過(guò)擬合限制模型復(fù)雜度、數(shù)據(jù)增強(qiáng)、交叉驗(yàn)證邏輯回歸收斂警告迭代次數(shù)不足max_iter調(diào)大到1000以上字符串標(biāo)簽報(bào)錯(cuò)需要數(shù)值標(biāo)簽用LabelEncoder對(duì)y編碼最后再說(shuō)一個(gè)我覺(jué)得對(duì)新手特別值得養(yǎng)成的習(xí)慣每次跑實(shí)驗(yàn)都把自己用到的參數(shù)、隨機(jī)種子、數(shù)據(jù)版本、最后得分記錄下來(lái)。不要覺(jué)得這是多余的我后來(lái)做項(xiàng)目調(diào)模型最痛苦的就是想復(fù)現(xiàn)一周前跑出的好結(jié)果卻完全不記得當(dāng)時(shí)是什么參數(shù)。這個(gè)習(xí)慣從入門(mén)第一天開(kāi)始建立你后面會(huì)感謝自己。