引言和數(shù)據(jù)
歡迎閱讀 Python 機(jī)器學(xué)習(xí)系列教程的回歸部分。這里,你應(yīng)該已經(jīng)安裝了 Scikit-Learn。如果沒有,安裝它,以及 Pandas 和 Matplotlib。
pip install numpy
pip install scipy
pip install scikit-learn
pip install matplotlib
pip install pandas
除了這些教程范圍的導(dǎo)入之外,我們還要在這里使用 Quandl:
pip install quandl
首先,對(duì)于我們將其用于機(jī)器學(xué)習(xí)而言,什么是回歸呢?它的目標(biāo)是接受連續(xù)數(shù)據(jù),尋找最適合數(shù)據(jù)的方程,并能夠?qū)μ囟ㄖ颠M(jìn)行預(yù)測(cè)。使用簡(jiǎn)單的線性回歸,你可以僅僅通過創(chuàng)建最佳擬合直線,來實(shí)現(xiàn)它。
?
這里,我們可以使用這條直線的方程,來預(yù)測(cè)未來的價(jià)格,其中日期是 x 軸。
回歸的熱門用法是預(yù)測(cè)股票價(jià)格。由于我們會(huì)考慮價(jià)格隨時(shí)間的流動(dòng),并且使用連續(xù)的數(shù)據(jù)集,嘗試預(yù)測(cè)未來的下一個(gè)流動(dòng)價(jià)格,所以可以這樣做。
回歸是監(jiān)督的機(jī)器學(xué)習(xí)的一種,也就是說,科學(xué)家向其展示特征,之后向其展示正確答案來教會(huì)機(jī)器。一旦教會(huì)了機(jī)器,科學(xué)家就能夠使用一些不可見的數(shù)據(jù)來測(cè)試機(jī)器,其中科學(xué)家知道正確答案,但是機(jī)器不知道。機(jī)器的答案會(huì)與已知答案對(duì)比,并且度量機(jī)器的準(zhǔn)確率。如果準(zhǔn)確率足夠高,科學(xué)家就會(huì)考慮將其算法用于真實(shí)世界。
由于回歸廣泛用于股票價(jià)格,我們可以使用一個(gè)示例從這里開始。最開始,我們需要數(shù)據(jù)。有時(shí)候數(shù)據(jù)易于獲取,有時(shí)你需要出去并親自收集。我們這里,我們至少能夠以簡(jiǎn)單的股票價(jià)格和成交量信息開始,它們來自 Quandl。我們會(huì)抓取 Google 的股票價(jià)格,它的代碼是GOOGL:
import pandas as pd
import quandl
df = quandl.get("WIKI/GOOGL")
print(df.head())
注意:寫這篇文章的時(shí)候,Quandl 的模塊使用大寫 Q 引用,但現(xiàn)在是小寫 q,所以import quandl。
到這里,我們擁有:
Open High Low Close Volume Ex-Dividend \
Date
2004-08-19 100.00 104.06 95.96 100.34 44659000 0
2004-08-20 101.01 109.08 100.50 108.31 22834300 0
2004-08-23 110.75 113.48 109.05 109.40 18256100 0
2004-08-24 111.24 111.60 103.57 104.87 15247300 0
2004-08-25 104.96 108.00 103.88 106.00 9188600 0
Split Ratio Adj. Open Adj. High Adj. Low Adj. Close \
Date
2004-08-19 1 50.000 52.03 47.980 50.170
2004-08-20 1 50.505 54.54 50.250 54.155
2004-08-23 1 55.375 56.74 54.525 54.700
2004-08-24 1 55.620 55.80 51.785 52.435
2004-08-25 1 52.480 54.00 51.940 53.000
Adj. Volume
Date
2004-08-19 44659000
2004-08-20 22834300
2004-08-23 18256100
2004-08-24 15247300
2004-08-25 9188600
這是個(gè)非常好的開始,我們擁有了數(shù)據(jù),但是有點(diǎn)多了。
這里,我們有很多列,許多都是多余的,還有些不怎么變化。我們可以看到,常規(guī)和修正(Adj)的列是重復(fù)的。修正的列看起來更加理想。常規(guī)的列是當(dāng)天的價(jià)格,但是股票有個(gè)叫做分拆的東西,其中一股突然就變成了兩股,所以一股的價(jià)格要減半,但是公司的價(jià)值不變。修正的列為股票分拆而調(diào)整,這使得它們對(duì)于分析更加可靠。
所以,讓我們繼續(xù),削減原始的 DataFrame。
df = df[['Adj. Open', 'Adj. High', 'Adj. Low', 'Adj. Close', 'Adj. Volume']]
現(xiàn)在我們擁有了修正的列,以及成交量。有一些東西需要注意。許多人談?wù)摶蛘呗犝f機(jī)器學(xué)習(xí),就像無中生有的黑魔法。機(jī)器學(xué)習(xí)可以突出已有的數(shù)據(jù),但是數(shù)據(jù)需要先存在。你需要有意義的數(shù)據(jù)。所以你怎么知道是否有意義呢?我的最佳建議就是,僅僅簡(jiǎn)化你的大腦??紤]一下,歷史價(jià)格會(huì)決定未來價(jià)格嗎?有些人這么認(rèn)為,但是久而久之這被證實(shí)是錯(cuò)誤的。但是歷史規(guī)律呢?突出的時(shí)候會(huì)有意義(機(jī)器學(xué)習(xí)會(huì)有所幫助),但是還是太弱了。那么,價(jià)格變化和成交量隨時(shí)間的關(guān)系,再加上歷史規(guī)律呢?可能更好一點(diǎn)。所以,你已經(jīng)能夠看到,并不是數(shù)據(jù)越多越好,而是我們需要使用有用處的數(shù)據(jù)。同時(shí),原始數(shù)據(jù)應(yīng)該做一些轉(zhuǎn)換。
考慮每日波動(dòng),例如最高價(jià)減最低價(jià)的百分比差值如何?每日的百分比變化又如何呢?你覺得Open, High, Low, Close這種簡(jiǎn)單數(shù)據(jù),還是Close, Spread/Volatility, %change daily更好?我覺得后者更好一點(diǎn)。前者都是非常相似的數(shù)據(jù)點(diǎn),后者基于前者的統(tǒng)一數(shù)據(jù)創(chuàng)建,但是帶有更加有價(jià)值的信息。
所以,并不是你擁有的所有數(shù)據(jù)都是有用的,并且有時(shí)你需要對(duì)你的數(shù)據(jù)執(zhí)行進(jìn)一步的操作,并使其更加有價(jià)值,之后才能提供給機(jī)器學(xué)習(xí)算法。讓我們繼續(xù)并轉(zhuǎn)換我們的數(shù)據(jù):
df['HL_PCT'] = (df['Adj. High'] - df['Adj. Low']) / df['Adj. Close'] * 100.0
這會(huì)創(chuàng)建一個(gè)新的列,它是基于收盤價(jià)的百分比極差,這是我們對(duì)于波動(dòng)的粗糙度量。下面,我們會(huì)計(jì)算每日百分比變化:
df['PCT_change'] = (df['Adj. Close'] - df['Adj. Open']) / df['Adj. Open'] * 100.0
現(xiàn)在我們會(huì)定義一個(gè)新的 DataFrame:
df = df[['Adj. Close', 'HL_PCT', 'PCT_change', 'Adj. Volume']]
print(df.head())
Adj. Close HL_PCT PCT_change Adj. Volume
Date
2004-08-19 50.170 8.072553 0.340000 44659000
2004-08-20 54.155 7.921706 7.227007 22834300
2004-08-23 54.700 4.049360 -1.218962 18256100
2004-08-24 52.435 7.657099 -5.726357 15247300
2004-08-25 53.000 3.886792 0.990854 9188600
特征和標(biāo)簽
基于上一篇機(jī)器學(xué)習(xí)回歸教程,我們將要對(duì)我們的股票價(jià)格數(shù)據(jù)執(zhí)行回歸。目前的代碼:
import quandl
import pandas as pd
df = quandl.get("WIKI/GOOGL")
df = df[['Adj. Open', 'Adj. High', 'Adj. Low', 'Adj. Close', 'Adj. Volume']]
df['HL_PCT'] = (df['Adj. High'] - df['Adj. Low']) / df['Adj. Close'] * 100.0
df['PCT_change'] = (df['Adj. Close'] - df['Adj. Open']) / df['Adj. Open'] * 100.0
df = df[['Adj. Close', 'HL_PCT', 'PCT_change', 'Adj. Volume']]
print(df.head())
這里我們已經(jīng)獲取了數(shù)據(jù),判斷出有價(jià)值的數(shù)據(jù),并通過操作創(chuàng)建了一些。我們現(xiàn)在已經(jīng)準(zhǔn)備好使用回歸開始機(jī)器學(xué)習(xí)的過程。首先,我們需要一些更多的導(dǎo)入。所有的導(dǎo)入是:
import quandl, math
import numpy as np
import pandas as pd
from sklearn import preprocessing, cross_validation, svm
from sklearn.linear_model import LinearRegression
我們會(huì)使用numpy模塊來將數(shù)據(jù)轉(zhuǎn)換為 NumPy 數(shù)組,它是 Sklearn 的預(yù)期。我們?cè)谟玫絧reprocessing和cross_validation時(shí),會(huì)深入談?wù)撍麄?,但是預(yù)處理是用于在機(jī)器學(xué)習(xí)之前,對(duì)數(shù)據(jù)清洗和縮放的模塊。交叉驗(yàn)證在測(cè)試階段使用。最后,我們也從 Sklearn 導(dǎo)入了LinearRegression算法,以及svm。它們用作我們的機(jī)器學(xué)習(xí)算法來展示結(jié)果。
這里,我們已經(jīng)獲取了我們認(rèn)為有用的數(shù)據(jù)。真實(shí)的機(jī)器學(xué)習(xí)如何工作呢?使用監(jiān)督式學(xué)習(xí),你需要特征和標(biāo)簽。特征就是描述性屬性,標(biāo)簽就是你嘗試預(yù)測(cè)的結(jié)果。另一個(gè)常見的回歸示例就是嘗試為某個(gè)人預(yù)測(cè)保險(xiǎn)的保費(fèi)。保險(xiǎn)公司會(huì)收集你的年齡、駕駛違規(guī)行為、公共犯罪記錄,以及你的信用評(píng)分。公司會(huì)使用老客戶,獲取數(shù)據(jù),并得出應(yīng)該給客戶的“理想保費(fèi)”,或者如果他們覺得有利可圖的話,他們會(huì)使用實(shí)際使用的客戶。
所以,對(duì)于訓(xùn)練機(jī)器學(xué)習(xí)分類器來說,特征是客戶屬性,標(biāo)簽是和這些屬性相關(guān)的保費(fèi)。
我們這里,什么是特征和標(biāo)簽?zāi)兀课覀儑L試預(yù)測(cè)價(jià)格,所以價(jià)格就是標(biāo)簽?如果這樣,什么是特征呢?對(duì)于預(yù)測(cè)我們的價(jià)格來說,我們的標(biāo)簽,就是我們打算預(yù)測(cè)的東西,實(shí)際上是未來價(jià)格。這樣,我們的特征實(shí)際上是:當(dāng)前價(jià)格、HL 百分比和百分比變化。標(biāo)簽價(jià)格是未來某個(gè)點(diǎn)的價(jià)格。讓我們繼續(xù)添加新的行:
forecast_col = 'Adj. Close'
df.fillna(value=-99999, inplace=True)
forecast_out = int(math.ceil(0.01 * len(df)))
這里,我們定義了預(yù)測(cè)列,之后我們將任何 NaN 數(shù)據(jù)填充為 -99999。對(duì)于如何處理缺失數(shù)據(jù),你有一些選擇,你不能僅僅將 NaN(不是數(shù)值)數(shù)據(jù)點(diǎn)傳給機(jī)器學(xué)習(xí)分類西,你需要處理它。一個(gè)主流選項(xiàng)就是將缺失值填充為 -99999。在許多機(jī)器學(xué)習(xí)分類器中,會(huì)將其是被為離群點(diǎn)。你也可以僅僅丟棄包含缺失值的所有特征或標(biāo)簽,但是這樣你可能會(huì)丟掉大量的數(shù)據(jù)。
真實(shí)世界中,許多數(shù)據(jù)集都很混亂。多數(shù)股價(jià)或成交量數(shù)據(jù)都很干凈,很少有缺失數(shù)據(jù),但是許多數(shù)據(jù)集會(huì)有大量缺失數(shù)據(jù)。我見過一些數(shù)據(jù)集,大量的行含有缺失數(shù)據(jù)。你并不一定想要失去所有不錯(cuò)的數(shù)據(jù),如果你的樣例數(shù)據(jù)有一些缺失,你可能會(huì)猜測(cè)真實(shí)世界的用例也有一些缺失。你需要訓(xùn)練、測(cè)試并依賴相同數(shù)據(jù),以及數(shù)據(jù)的特征。
最后,我們定義我們需要預(yù)測(cè)的東西。許多情況下,就像嘗試預(yù)測(cè)客戶的保費(fèi)的案例中,你僅僅需要一個(gè)數(shù)字,但是對(duì)于預(yù)測(cè)來說,你需要預(yù)測(cè)指定數(shù)量的數(shù)據(jù)點(diǎn)。我們假設(shè)我們打算預(yù)測(cè)數(shù)據(jù)集整個(gè)長度的 1%。因此,如果我們的數(shù)據(jù)是 100 天的股票價(jià)格,我們需要能夠預(yù)測(cè)未來一天的價(jià)格。選擇你想要的那個(gè)。如果你只是嘗試預(yù)測(cè)明天的價(jià)格,你應(yīng)該選取一天之后的數(shù)據(jù),而且也只能一天之后的數(shù)據(jù)。如果你打算預(yù)測(cè) 10 天,我們可以為每一天生成一個(gè)預(yù)測(cè)。
我們這里,我們決定了,特征是一系列當(dāng)前值,標(biāo)簽是未來的價(jià)格,其中未來是數(shù)據(jù)集整個(gè)長度的 1%。我們假設(shè)所有當(dāng)前列都是我們的特征,所以我們使用一個(gè)簡(jiǎn)單的 Pnadas 操作添加一個(gè)新的列:
df['label'] = df[forecast_col].shift(-forecast_out)
現(xiàn)在我們擁有了數(shù)據(jù),包含特征和標(biāo)簽。下面我們?cè)趯?shí)際運(yùn)行任何東西之前,我們需要做一些預(yù)處理和最終步驟,我們?cè)谙乱黄坛虝?huì)關(guān)注。
訓(xùn)練和測(cè)試
歡迎閱讀 python 機(jī)器學(xué)習(xí)系列教程的第四部分。在上一個(gè)教程中,我們獲取了初始數(shù)據(jù),按照我們的喜好操作和轉(zhuǎn)換數(shù)據(jù),之后我們定義了我們的特征。Scikit 不需要處理 Pandas 和 DataFrame,我出于自己的喜好而處理它,因?yàn)樗觳⑶腋咝?。反之,Sklearn 實(shí)際上需要 NumPy 數(shù)組。Pandas 的 DataFrame 可以輕易轉(zhuǎn)換為 NumPy 數(shù)組,所以事情就是這樣的。
目前為止我們的代碼:
import quandl, math
import numpy as np
import pandas as pd
from sklearn import preprocessing, cross_validation, svm
from sklearn.linear_model import LinearRegression
df = quandl.get("WIKI/GOOGL")
print(df.head())
#print(df.tail())
df = df[['Adj. Open', 'Adj. High', 'Adj. Low', 'Adj. Close', 'Adj. Volume']]
df['HL_PCT'] = (df['Adj. High'] - df['Adj. Low']) / df['Adj. Close'] * 100.0
df['PCT_change'] = (df['Adj. Close'] - df['Adj. Open']) / df['Adj. Open'] * 100.0
df = df[['Adj. Close', 'HL_PCT', 'PCT_change', 'Adj. Volume']]
print(df.head())
forecast_col = 'Adj. Close'
df.fillna(value=-99999, inplace=True)
forecast_out = int(math.ceil(0.01 * len(df)))
df['label'] = df[forecast_col].shift(-forecast_out)
我們之后要丟棄所有仍舊是 NaN 的信息。
df.dropna(inplace=True)
對(duì)于機(jī)器學(xué)習(xí)來說,通常要定義X(大寫)作為特征,和y(小寫)作為對(duì)于特征的標(biāo)簽。這樣,我們可以定義我們的特征和標(biāo)簽,像這樣:
X = np.array(df.drop(['label'], 1))
y = np.array(df['label'])
上面,我們所做的就是定義X(特征),是我們整個(gè)的 DataFrame,除了label列,并轉(zhuǎn)換為 NumPy 數(shù)組。我們使用drop方法,可以用于 DataFrame,它返回一個(gè)新的 DataFrame。下面,我們定義我們的y變量,它是我們的標(biāo)簽,僅僅是 DataFrame 的標(biāo)簽列,并轉(zhuǎn)換為 NumPy 數(shù)組。
現(xiàn)在我們就能告一段落,轉(zhuǎn)向訓(xùn)練和測(cè)試了,但是我們打算做一些預(yù)處理。通常,你希望你的特征在 -1 到 1 的范圍內(nèi)。這可能不起作用,但是通常會(huì)加速處理過程,并有助于準(zhǔn)確性。因?yàn)榇蠹叶际褂眠@個(gè)范圍,它包含在了 Sklearn 的preprocessing模塊中。為了使用它,你需要對(duì)你的X變量調(diào)用preprocessing.scale。
X = preprocessing.scale(X)
下面,創(chuàng)建標(biāo)簽y:
y = np.array(df['label'])
現(xiàn)在就是訓(xùn)練和測(cè)試的時(shí)候了。方式就是選取 75% 的數(shù)據(jù)用于訓(xùn)練機(jī)器學(xué)習(xí)分類器。之后選取剩下的 25% 的數(shù)據(jù)用于測(cè)試分類器。由于這是你的樣例數(shù)據(jù),你應(yīng)該擁有特征和一直標(biāo)簽。因此,如果你測(cè)試后 25% 的數(shù)據(jù),你就會(huì)得到一種準(zhǔn)確度和可靠性,叫做置信度。有許多方式可以實(shí)現(xiàn)它,但是,最好的方式可能就是使用內(nèi)建的cross_validation,因?yàn)樗矔?huì)為你打亂數(shù)據(jù)。代碼是這樣:
X_train, X_test, y_train, y_test = cross_validation.train_test_split(X, y, test_size=0.2)
這里的返回值是特征的訓(xùn)練集、測(cè)試集、標(biāo)簽的訓(xùn)練集和測(cè)試集?,F(xiàn)在,我們已經(jīng)定義好了分類器。Sklearn 提供了許多通用的分類器,有一些可以用于回歸。我們會(huì)在這個(gè)例子中展示一些,但是現(xiàn)在,讓我們使用svm包中的支持向量回歸。
clf = svm.SVR()
我們這里僅僅使用默認(rèn)選項(xiàng)來使事情簡(jiǎn)單,但是你可以在sklearn.svm.SVR的文檔中了解更多。
一旦你定義了分類器,你就可以訓(xùn)練它了。在 Sklearn 中,使用fit來訓(xùn)練。
clf.fit(X_train, y_train)
這里,我們擬合了我們的訓(xùn)練特征和訓(xùn)練標(biāo)簽。
我們的分類器現(xiàn)在訓(xùn)練完畢。這非常簡(jiǎn)單,現(xiàn)在我們可以測(cè)試了。
confidence = clf.score(X_test, y_test)
加載測(cè)試,之后:
print(confidence)
# 0.960075071072
所以這里,我們可以看到準(zhǔn)確率幾乎是 96%。沒有什么可說的,讓我們嘗試另一個(gè)分類器,這一次使用LinearRegression:
clf = LinearRegression()
# 0.963311624499
更好一點(diǎn),但是基本一樣。所以作為科學(xué)家,我們?nèi)绾沃?,選擇哪個(gè)算法呢?不久,你會(huì)熟悉什么在多數(shù)情況下都工作,什么不工作。你可以從 Scikit 的站點(diǎn)上查看選擇正確的評(píng)估工具。這有助于你瀏覽一些基本的選項(xiàng)。如果你詢問搞機(jī)器學(xué)習(xí)的人,它完全是試驗(yàn)和出錯(cuò)。你會(huì)嘗試大量的算法并且僅僅選取最好的那個(gè)。要注意的另一件事情就是,一些算法必須線性運(yùn)行,其它的不是。不要把線性回歸和線性運(yùn)行搞混了。所以這些意味著什么呢?一些機(jī)器學(xué)習(xí)算法會(huì)一次處理一步,沒有多線程,其它的使用多線程,并且可以利用你機(jī)器上的多核。你可以深入了解每個(gè)算法,來弄清楚哪個(gè)可以多線程,或者你可以閱讀文檔,并查看n_jobs參數(shù)。如果擁有n_jobs,你就可以讓算法通過多線程來獲取更高的性能。如果沒有,就很不走運(yùn)了。所以,如果你處理大量的數(shù)據(jù),或者需要處理中等規(guī)模的數(shù)據(jù),但是需要很高的速度,你就可能想要線程加速。讓我們看看這兩個(gè)算法。
訪問sklearn.svm.SVR的文檔,并查看參數(shù),看到n_jobs了嘛?反正我沒看到,所以它就不能使用線程。你可能會(huì)看到,在我們的小型數(shù)據(jù)集上,差異不大。但是,假設(shè)數(shù)據(jù)集由 20MB,差異就很明顯。然后,我們查看LinearRegression算法,看到n_jobs了嘛?當(dāng)然,所以這里,你可以指定你希望多少線程。如果你傳入-1,算法會(huì)使用所有可用的線程。
這樣:
clf = LinearRegression(n_jobs=-1)
就夠了。雖然我讓你做了很少的事情(查看文檔),讓我給你說個(gè)事實(shí)吧,僅僅由于機(jī)器學(xué)習(xí)算法使用默認(rèn)參數(shù)工作,不代表你可以忽略它們。例如,讓我們回顧svm.SVR。SVR 是支持向量回歸,在執(zhí)行機(jī)器學(xué)習(xí)時(shí),它是一種架構(gòu)。我非常鼓勵(lì)那些有興趣學(xué)習(xí)更多的人,去研究這個(gè)主題,以及向比我學(xué)歷更高的人學(xué)習(xí)基礎(chǔ)。我會(huì)盡力把東西解釋得更簡(jiǎn)單,但是我并不是專家?;氐絼偛诺?a href="http://www.wenjunhu.com/v/tag/" target="_blank">話題,svm.SVR有一個(gè)參數(shù)叫做kernel。這個(gè)是什么呢?核就相當(dāng)于你的數(shù)據(jù)的轉(zhuǎn)換。這使得處理過程更加迅速。在svm.SVR的例子中,默認(rèn)值是rbf,這是核的一個(gè)類型,你有一些選擇。查看文檔,你可以選擇'linear', 'poly', 'rbf', 'sigmoid', 'precomputed'或者一個(gè)可調(diào)用對(duì)象。同樣,就像嘗試不同的 ML 算法一樣,你可以做你想做的任何事情,嘗試一下不同的核吧。
for k in ['linear','poly','rbf','sigmoid']:
clf = svm.SVR(kernel=k)
clf.fit(X_train, y_train)
confidence = clf.score(X_test, y_test)
print(k,confidence)
linear 0.960075071072
poly 0.63712232551
rbf 0.802831714511
sigmoid -0.125347960903
我們可以看到,線性的核表現(xiàn)最好,之后是rbf,之后是poly,sigmoid很顯然是個(gè)擺設(shè),并且應(yīng)該移除。
所以我們訓(xùn)練并測(cè)試了數(shù)據(jù)集。我們已經(jīng)有 71% 的滿意度了。下面我們做什么呢?現(xiàn)在我們需要再進(jìn)一步,做一些預(yù)測(cè),下一章會(huì)涉及它。
預(yù)測(cè)
歡迎閱讀機(jī)器學(xué)習(xí)系列教程的第五章,當(dāng)前涉及到回歸。目前為止,我們收集并修改了數(shù)據(jù),訓(xùn)練并測(cè)試了分類器。這一章中,我們打算使用我們的分類器來實(shí)際做一些預(yù)測(cè)。我們目前所使用的代碼為:
import quandl, math
import numpy as np
import pandas as pd
from sklearn import preprocessing, cross_validation, svm
from sklearn.linear_model import LinearRegression
df = quandl.get("WIKI/GOOGL")
df = df[['Adj. Open', 'Adj. High', 'Adj. Low', 'Adj. Close', 'Adj. Volume']]
df['HL_PCT'] = (df['Adj. High'] - df['Adj. Low']) / df['Adj. Close'] * 100.0
df['PCT_change'] = (df['Adj. Close'] - df['Adj. Open']) / df['Adj. Open'] * 100.0
df = df[['Adj. Close', 'HL_PCT', 'PCT_change', 'Adj. Volume']]
forecast_col = 'Adj. Close'
df.fillna(value=-99999, inplace=True)
forecast_out = int(math.ceil(0.01 * len(df)))
df['label'] = df[forecast_col].shift(-forecast_out)
X = np.array(df.drop(['label'], 1))
X = preprocessing.scale(X)
X = X[:-forecast_out]
df.dropna(inplace=True)
y = np.array(df['label'])
X_train, X_test, y_train, y_test = cross_validation.train_test_split(X, y, test_size=0.2)
clf = LinearRegression(n_jobs=-1)
clf.fit(X_train, y_train)
confidence = clf.score(X_test, y_test)
print(confidence)
我會(huì)強(qiáng)調(diào),準(zhǔn)確率大于 95% 的線性模型并不是那么好。我當(dāng)然不會(huì)用它來交易股票。仍然有一些需要考慮的問題,特別是不同公司有不同的價(jià)格軌跡。Google 非常線性,向右上角移動(dòng),許多公司不是這樣,所以要記住?,F(xiàn)在,為了做預(yù)測(cè),我們需要一些數(shù)據(jù)。我們決定預(yù)測(cè) 1% 的數(shù)據(jù),因此我們打算,或者至少能夠預(yù)測(cè)數(shù)據(jù)集的后 1%。所以我們什么可以這樣做呢?我們什么時(shí)候可以識(shí)別這些數(shù)據(jù)?我們現(xiàn)在就可以,但是要注意我們嘗試預(yù)測(cè)的數(shù)據(jù),并沒有像訓(xùn)練集那樣縮放。好的,那么做什么呢?是否要對(duì)后 1% 調(diào)用preprocessing.scale()?縮放方法基于所有給它的已知數(shù)據(jù)集。理想情況下,你應(yīng)該一同縮放訓(xùn)練集、測(cè)試集和用于預(yù)測(cè)的數(shù)據(jù)。這永遠(yuǎn)是可能或合理的嘛?不是,如果你可以這么做,你就應(yīng)該這么做。但是,我們這里,我們可以這么做。我們的數(shù)據(jù)足夠小,并且處理時(shí)間足夠低,所以我們會(huì)一次性預(yù)處理并縮放數(shù)據(jù)。
在許多例子中,你不能這么做。想象如果你使用幾個(gè) GB 的數(shù)據(jù)來訓(xùn)練分類器。訓(xùn)練分類器會(huì)花費(fèi)幾天,不能在每次想要做出預(yù)測(cè)的時(shí)候都這么做。因此,你可能需要不縮放任何東西,或者單獨(dú)縮放數(shù)據(jù)。通常,你可能希望測(cè)試這兩個(gè)選項(xiàng),并看看那個(gè)對(duì)于你的特定案例更好。
要記住它,讓我們?cè)诙xX的時(shí)候處理所有行:
X = np.array(df.drop(['label'], 1))
X = preprocessing.scale(X)
X_lately = X[-forecast_out:]
X = X[:-forecast_out]
df.dropna(inplace=True)
y = np.array(df['label'])
X_train, X_test, y_train, y_test = cross_validation.train_test_split(X, y, test_size=0.2)
clf = LinearRegression(n_jobs=-1)
clf.fit(X_train, y_train)
confidence = clf.score(X_test, y_test)
print(confidence)
要注意我們首先獲取所有數(shù)據(jù),預(yù)處理,之后再分割。我們的X_lately變量包含最近的特征,我們需要對(duì)其進(jìn)行預(yù)測(cè)。目前你可以看到,定義分類器、訓(xùn)練、和測(cè)試都非常簡(jiǎn)單。預(yù)測(cè)也非常簡(jiǎn)單:
forecast_set = clf.predict(X_lately)
forecast_set是預(yù)測(cè)值的數(shù)組,表明你不僅僅可以做出單個(gè)預(yù)測(cè),還可以一次性預(yù)測(cè)多個(gè)值??纯次覀兡壳皳碛惺裁矗?br />
[ 745.67829395 737.55633261 736.32921413 717.03929303 718.59047951
731.26376715 737.84381394 751.28161162 756.31775293 756.76751056
763.20185946 764.52651181 760.91320031 768.0072636 766.67038016
763.83749414 761.36173409 760.08514166 770.61581391 774.13939706
768.78733341 775.04458624 771.10782342 765.13955723 773.93369548
766.05507556 765.4984563 763.59630529 770.0057166 777.60915879] 0.956987938167 30
所以這些就是我們的預(yù)測(cè)結(jié)果,然后呢?已經(jīng)基本完成了,但是我們可以將其可視化。股票價(jià)格是每一天的,一周 5 天,周末沒有。我知道這個(gè)事實(shí),但是我們打算將其簡(jiǎn)化,把每個(gè)預(yù)測(cè)值當(dāng)成每一天的。如果你打算處理周末的間隔(不要忘了假期),就去做吧,但是我這里會(huì)將其簡(jiǎn)化。最開始,我們添加一些新的導(dǎo)入:
import datetime
import matplotlib.pyplot as plt
from matplotlib import style
我導(dǎo)入了datetime來處理datetime對(duì)象,Matplotlib 的pyplot包用于繪圖,以及style來使我們的繪圖更加時(shí)髦。讓我們?cè)O(shè)置一個(gè)樣式:
style.use('ggplot')
之后,我們添加一個(gè)新的列,forecast列:
df['Forecast'] = np.nan
我們首先將值設(shè)置為 NaN,但是我們之后會(huì)填充他。
預(yù)測(cè)集的標(biāo)簽正好從明天開始。因?yàn)槲覀円A(yù)測(cè)未來m = 0.1 * len(df)天的數(shù)據(jù),相當(dāng)于把收盤價(jià)往前移動(dòng)m天生成標(biāo)簽。那么數(shù)據(jù)集的后m個(gè)是不能用作訓(xùn)練集和測(cè)試集的,因?yàn)闆]有標(biāo)簽。于是我們將后m個(gè)數(shù)據(jù)用作預(yù)測(cè)集。預(yù)測(cè)集的第一個(gè)數(shù)據(jù),也就是數(shù)據(jù)集的第n - m個(gè)數(shù)據(jù),它的標(biāo)簽應(yīng)該是n - m + m = n天的收盤價(jià),我們知道今天在df里面是第n - 1天,那么它就是明天。
我們首先需要抓取 DataFrame 的最后一天,將每一個(gè)新的預(yù)測(cè)值賦給新的日期。我們會(huì)這樣開始。
last_date = df.iloc[-1].name
last_unix = last_date.timestamp()
one_day = 86400
next_unix = last_unix + one_day
現(xiàn)在我們擁有了預(yù)測(cè)集的起始日期,并且一天有 86400 秒?,F(xiàn)在我們將預(yù)測(cè)添加到現(xiàn)有的 DataFrame 中。
for i in forecast_set:
next_date = datetime.datetime.fromtimestamp(next_unix)
next_unix += 86400
df.loc[next_date] = [np.nan for _ in range(len(df.columns)-1)]+[i]
我們這里所做的是,迭代預(yù)測(cè)集的標(biāo)簽,獲取每個(gè)預(yù)測(cè)值和日期,之后將這些值放入 DataFrame(使預(yù)測(cè)集的特征為 NaN)。最后一行的代碼創(chuàng)建 DataFrame 中的一行,所有元素置為 NaN,然后將最后一個(gè)元素置為i(這里是預(yù)測(cè)集的標(biāo)簽)。我選擇了這種單行的for循環(huán),以便在改動(dòng) DataFrame 和特征之后,代碼還能正常工作。所有東西都做完了嗎?將其繪制出來。
df['Adj. Close'].plot()
df['Forecast'].plot()
plt.legend(loc=4)
plt.xlabel('Date')
plt.ylabel('Price')
plt.show()
完整的代碼:
import Quandl, math
import numpy as np
import pandas as pd
from sklearn import preprocessing, cross_validation, svm
from sklearn.linear_model import LinearRegression
import matplotlib.pyplot as plt
from matplotlib import style
import datetime
style.use('ggplot')
df = Quandl.get("WIKI/GOOGL")
df = df[['Adj. Open', 'Adj. High', 'Adj. Low', 'Adj. Close', 'Adj. Volume']]
df['HL_PCT'] = (df['Adj. High'] - df['Adj. Low']) / df['Adj. Close'] * 100.0
df['PCT_change'] = (df['Adj. Close'] - df['Adj. Open']) / df['Adj. Open'] * 100.0
df = df[['Adj. Close', 'HL_PCT', 'PCT_change', 'Adj. Volume']]
forecast_col = 'Adj. Close'
df.fillna(value=-99999, inplace=True)
forecast_out = int(math.ceil(0.01 * len(df)))
df['label'] = df[forecast_col].shift(-forecast_out)
X = np.array(df.drop(['label'], 1))
X = preprocessing.scale(X)
X_lately = X[-forecast_out:]
X = X[:-forecast_out]
df.dropna(inplace=True)
y = np.array(df['label'])
X_train, X_test, y_train, y_test = cross_validation.train_test_split(X, y, test_size=0.2)
clf = LinearRegression(n_jobs=-1)
clf.fit(X_train, y_train)
confidence = clf.score(X_test, y_test)
forecast_set = clf.predict(X_lately)
df['Forecast'] = np.nan
last_date = df.iloc[-1].name
last_unix = last_date.timestamp()
one_day = 86400
next_unix = last_unix + one_day
for i in forecast_set:
next_date = datetime.datetime.fromtimestamp(next_unix)
next_unix += 86400
df.loc[next_date] = [np.nan for _ in range(len(df.columns)-1)]+[i]
df['Adj. Close'].plot()
df['Forecast'].plot()
plt.legend(loc=4)
plt.xlabel('Date')
plt.ylabel('Price')
plt.show()
結(jié)果:
?
保存和擴(kuò)展
上一篇教程中,我們使用回歸完成了對(duì)股票價(jià)格的預(yù)測(cè),并使用 Matplotlib 可視化。這個(gè)教程中,我們會(huì)討論一些接下來的步驟。
我記得我第一次嘗試學(xué)習(xí)機(jī)器學(xué)習(xí)的時(shí)候,多數(shù)示例僅僅涉及到訓(xùn)練和測(cè)試的部分,完全跳過了預(yù)測(cè)部分。對(duì)于那些包含訓(xùn)練、測(cè)試和預(yù)測(cè)部分的教程來說,我沒有找到一篇解釋保存算法的文章。在那些例子中,數(shù)據(jù)通常非常小,所以訓(xùn)練、測(cè)試和預(yù)測(cè)過程都很快。在真實(shí)世界中,數(shù)據(jù)都非常大,并且花費(fèi)更長時(shí)間來處理。由于沒有一篇教程真正談?wù)摰竭@一重要的過程,我打算包含一些處理時(shí)間和保存算法的信息。
雖然我們的機(jī)器學(xué)習(xí)分類器花費(fèi)幾秒來訓(xùn)練,在一些情況下,訓(xùn)練分類器需要幾個(gè)小時(shí)甚至是幾天。想象你想要預(yù)測(cè)價(jià)格的每天都需要這么做。這不是必要的,因?yàn)槲覀兡乜梢允褂?Pickle 模塊來保存分類器。首先確保你導(dǎo)入了它:
import pickle
使用 Pickle,你可以保存 Python 對(duì)象,就像我們的分類器那樣。在定義、訓(xùn)練和測(cè)試你的分類器之后,添加:
with open('linearregression.pickle','wb') as f:
pickle.dump(clf, f)
現(xiàn)在,再次執(zhí)行腳本,你應(yīng)該得到了linearregression.pickle,它是分類器的序列化數(shù)據(jù)?,F(xiàn)在,你需要做的所有事情就是加載pickle文件,將其保存到clf,并照常使用,例如:
pickle_in = open('linearregression.pickle','rb')
clf = pickle.load(pickle_in)
代碼中:
import Quandl, math
import numpy as np
import pandas as pd
from sklearn import preprocessing, cross_validation, svm
from sklearn.linear_model import LinearRegression
import matplotlib.pyplot as plt
from matplotlib import style
import datetime
import pickle
style.use('ggplot')
df = Quandl.get("WIKI/GOOGL")
df = df[['Adj. Open', 'Adj. High', 'Adj. Low', 'Adj. Close', 'Adj. Volume']]
df['HL_PCT'] = (df['Adj. High'] - df['Adj. Low']) / df['Adj. Close'] * 100.0
df['PCT_change'] = (df['Adj. Close'] - df['Adj. Open']) / df['Adj. Open'] * 100.0
df = df[['Adj. Close', 'HL_PCT', 'PCT_change', 'Adj. Volume']]
forecast_col = 'Adj. Close'
df.fillna(value=-99999, inplace=True)
forecast_out = int(math.ceil(0.1 * len(df)))
df['label'] = df[forecast_col].shift(-forecast_out)
X = np.array(df.drop(['label'], 1))
X = preprocessing.scale(X)
X_lately = X[-forecast_out:]
X = X[:-forecast_out]
df.dropna(inplace=True)
y = np.array(df['label'])
X_train, X_test, y_train, y_test = cross_validation.train_test_split(X, y, test_size=0.2)
#COMMENTED OUT:
##clf = svm.SVR(kernel='linear')
##clf.fit(X_train, y_train)
##confidence = clf.score(X_test, y_test)
##print(confidence)
pickle_in = open('linearregression.pickle','rb')
clf = pickle.load(pickle_in)
forecast_set = clf.predict(X_lately)
df['Forecast'] = np.nan
last_date = df.iloc[-1].name
last_unix = last_date.timestamp()
one_day = 86400
next_unix = last_unix + one_day
for i in forecast_set:
next_date = datetime.datetime.fromtimestamp(next_unix)
next_unix += 86400
df.loc[next_date] = [np.nan for _ in range(len(df.columns)-1)]+[i]
df['Adj. Close'].plot()
df['Forecast'].plot()
plt.legend(loc=4)
plt.xlabel('Date')
plt.ylabel('Price')
plt.show()
?
?
要注意我們注釋掉了分類器的原始定義,并替換為加載我們保存的分類器。就是這么簡(jiǎn)單。
最后,我們要討論一下效率和保存時(shí)間,前幾天我打算提出一個(gè)相對(duì)較低的范式,這就是臨時(shí)的超級(jí)計(jì)算機(jī)。嚴(yán)肅地說,隨著按需主機(jī)服務(wù)的興起,例如 AWS、DO 和 Linode,你能夠按照小時(shí)來購買主機(jī)。虛擬服務(wù)器可以在 60 秒內(nèi)建立,所需的模塊可以在 15 分鐘內(nèi)安裝,所以非常有限。你可以寫一個(gè) shell 腳本或者什么東西來給它加速??紤]你需要大量的處理,并且還沒有一臺(tái)頂級(jí)計(jì)算機(jī),或者你使用筆記本。沒有問題,只需要啟動(dòng)一臺(tái)服務(wù)器。
我對(duì)這個(gè)方式的最后一個(gè)注解是,使用任何主機(jī),你通常都可以建立一個(gè)非常小型的服務(wù)器,加載所需的東西,之后擴(kuò)展這個(gè)服務(wù)器。我喜歡以一個(gè)小型服務(wù)器開始,之后,我準(zhǔn)備好的時(shí)候,我會(huì)改變它的尺寸,給它升級(jí)。完成之后,不要忘了注銷或者降級(jí)你的服務(wù)器。
理論以及工作原理
歡迎閱讀第七篇教程。目前為止,你已經(jīng)看到了線性回歸的價(jià)值,以及如何使用 Sklearn 來應(yīng)用它。現(xiàn)在我們打算深入了解它如何計(jì)算。雖然我覺得不必要深入到每個(gè)機(jī)器學(xué)習(xí)算法數(shù)學(xué)中(你有沒有進(jìn)入到你最喜歡的模塊的源碼中,看看它是如何實(shí)現(xiàn)的?),線性代數(shù)是機(jī)器學(xué)習(xí)的本質(zhì),并且對(duì)于理解機(jī)器學(xué)習(xí)的構(gòu)建基礎(chǔ)十分實(shí)用。
線性代數(shù)的目標(biāo)是計(jì)算向量空間中的點(diǎn)的關(guān)系。這可以用于很多事情,但是某天,有個(gè)人有了個(gè)非??褚暗南敕?,拿他處理數(shù)據(jù)集的特征。我們也可以。記得之前我們定義數(shù)據(jù)類型的時(shí)候,線性回歸處理連續(xù)數(shù)據(jù)嗎?這并不是因?yàn)槭褂镁€性回歸的人,而是因?yàn)榻M成它的數(shù)學(xué)。簡(jiǎn)單的線性回歸可用于尋找數(shù)據(jù)集的最佳擬合直線。如果數(shù)據(jù)不是連續(xù)的,就不是最佳擬合直線。讓我們看看一些示例。
協(xié)方差
?
上面的圖像顯然擁有良好的協(xié)方差。如果你通過估計(jì)畫一條最佳擬合直線,你應(yīng)該能夠輕易畫出來:
?
如果圖像是這樣呢?
?
并不和之前一樣,但是是清楚的負(fù)相關(guān)。你可能能夠畫出最佳擬合直線,但是更可能畫不出來。
最后,這個(gè)呢?
?
啥?的確有最佳擬合直線,但是需要運(yùn)氣將其畫出來。
將上面的圖像看做特征的圖像,所以 X 坐標(biāo)是特征,Y 坐標(biāo)是相關(guān)的標(biāo)簽。X 和 Y 是否有任何形式的結(jié)構(gòu)化關(guān)系呢?雖然我們可以準(zhǔn)確計(jì)算關(guān)系,未來我們就不太可能擁有這么多值了。
在其它圖像的案例中,X 和 Y 之間顯然存在關(guān)系。我們實(shí)際上可以探索這種關(guān)系,之后沿著我們希望的任何點(diǎn)繪圖。我們可以拿 Y 來預(yù)測(cè) X,或者拿 X 來預(yù)測(cè) Y,對(duì)于任何我們可以想到的點(diǎn)。我們也可以預(yù)測(cè)我們的模型有多少的誤差,即使模型只有一個(gè)點(diǎn)。我們?nèi)绾螌?shí)現(xiàn)這個(gè)魔法呢?當(dāng)然是線性代數(shù)。
首先,讓我們回到中學(xué),我們?cè)谀抢飶?fù)習(xí)直線的定義:y = mx + b,其中m是斜率,b是縱截距。這可以是用于求解y的方程,我們可以將其變形來求解x,使用基本的代數(shù)原則:x = (y-b)/m。
好的,所以,我們的目標(biāo)是尋找最佳擬合直線。不是僅僅是擬合良好的直線,而是最好的那條。這條直線的定義就是y = mx + b。y就是答案(我們其他的坐標(biāo),或者甚至是我們的特征),所以我們?nèi)匀恍枰猰(斜率)和b(縱截距),由于x可能為沿 x 軸的任一點(diǎn),所以它是已知的。
最佳擬合直線的斜率m定義為:
?
注:可簡(jiǎn)寫為m = cov(x, y) / var(x)。
符號(hào)上面的橫杠代表均值。如果兩個(gè)符號(hào)挨著,就將其相乘。xs 和 ys 是所有已知坐標(biāo)。所以我們現(xiàn)在求出了y=mx+b最佳擬合直線定義的m(斜率),現(xiàn)在我們僅僅需要b(縱截距)。這里是公式:
好的。整個(gè)部分不是個(gè)數(shù)學(xué)教程,而是個(gè)編程教程。下一個(gè)教程中,我們打算這樣做,并且解釋為什么我要編程實(shí)現(xiàn)它,而不是直接用模塊。
編程計(jì)算斜率
歡迎閱讀第八篇教程,我們剛剛意識(shí)到,我們需要使用 Python 重復(fù)編寫一些比較重要的算法,來嘗試給定數(shù)據(jù)集的計(jì)算最佳擬合直線。
在我們開始之前,為什么我們會(huì)有一些小麻煩呢?線性回歸是機(jī)器學(xué)習(xí)的構(gòu)建基礎(chǔ)。它幾乎用于每個(gè)單獨(dú)的主流機(jī)器學(xué)習(xí)算法之中,所以對(duì)它的理解有助于你掌握多數(shù)主流機(jī)器學(xué)習(xí)算法。出于我們的熱情,理解線性回歸和線性代數(shù),是編寫你自己的機(jī)器學(xué)習(xí)算法,以及跨入機(jī)器學(xué)習(xí)前沿,使用當(dāng)前最佳的處理過程的第一步。由于處理過程的優(yōu)化和硬件架構(gòu)的改變。用于機(jī)器學(xué)習(xí)的方法論也會(huì)改變。最近出現(xiàn)的神經(jīng)網(wǎng)絡(luò),使用大量 GPU 來完成工作。你想知道什么是神經(jīng)網(wǎng)絡(luò)的核心嗎?你猜對(duì)了,線性代數(shù)。
如果你能記得,最佳擬合直線的斜率m:
?
是的,我們會(huì)將其拆成片段。首先,進(jìn)行一些導(dǎo)入:
from statistics import mean
import numpy as np
我們從statistics導(dǎo)入mean,所以我們可以輕易獲取列表的均值。下面,我們使numpy as np,所以我們可以其創(chuàng)建 NumPy 數(shù)組。我們可以對(duì)列表做很多事情,但是我們需要能夠做一些簡(jiǎn)單的矩陣運(yùn)算,它并不對(duì)簡(jiǎn)單列表提供,所以我們使用 NumPy。我們?cè)谶@個(gè)階段不會(huì)使用太復(fù)雜的 NumPy,但是之后 NumPy 就會(huì)成為你的最佳伙伴。下面,讓我們定義一些起始點(diǎn)吧。
xs = [1,2,3,4,5]
ys = [5,4,6,5,6]
所以這里有一些我們要使用的數(shù)據(jù)點(diǎn),xs和ys。你可以認(rèn)為xs就是特征,ys就是標(biāo)簽,或者他們都是特征,我們想要建立他們的聯(lián)系。之前提到過,我們實(shí)際上把它們變成 NumPy 數(shù)組,以便執(zhí)行矩陣運(yùn)算。所以讓我們修改這兩行:
xs = np.array([1,2,3,4,5], dtype=np.float64)
ys = np.array([5,4,6,5,6], dtype=np.float64)
現(xiàn)在他們都是 NumPy 數(shù)組了。我們也顯式聲明了數(shù)據(jù)類型。簡(jiǎn)單講一下,數(shù)據(jù)類型有特性是屬性,這些屬性決定了數(shù)據(jù)本身如何儲(chǔ)存和操作?,F(xiàn)在它不是什么問題,但是如果我們執(zhí)行大量運(yùn)算,并希望他們跑在 GPU 而不是 CPU 上就是了。
將其畫出來,他們是:
?
現(xiàn)在我們準(zhǔn)備好構(gòu)建函數(shù)來計(jì)算m,也就是我們的直線斜率:
def best_fit_slope(xs,ys):
return m
m = best_fit_slope(xs,ys)
好了。開個(gè)玩笑,所以這是我們的框架,現(xiàn)在我們要填充了。
我們的第一個(gè)邏輯就是計(jì)算xs的均值,再乘上ys的均值。繼續(xù)填充我們的框架:
def best_fit_slope(xs,ys):
m = (mean(xs) * mean(ys))
return m
目前為止還很簡(jiǎn)單。你可以對(duì)列表、元組或者數(shù)組使用mean函數(shù)。要注意我這里使用了括號(hào)。Python 的遵循運(yùn)算符的數(shù)學(xué)優(yōu)先級(jí)。所以如果你打算保證順序,要顯式使用括號(hào)。要記住你的運(yùn)算規(guī)則。
下面我們需要將其減去x*y的均值。這既是我們的矩陣運(yùn)算mean(xs*ys)?,F(xiàn)在的代碼是:
def best_fit_slope(xs,ys):
m = ( (mean(xs)*mean(ys)) - mean(xs*ys) )
return m
我們完成了公式的分子部分,現(xiàn)在我們繼續(xù)處理的分母,以x的均值平方開始:(mean(xs)*mean(xs))。Python 支持** 2,能夠處理我們的 NumPy 數(shù)組的float64類型。添加這些東西:
def best_fit_slope(xs,ys):
m = ( ((mean(xs)*mean(ys)) - mean(xs*ys)) /
(mean(xs)**2))
return m
雖然根據(jù)運(yùn)算符優(yōu)先級(jí),向整個(gè)表達(dá)式添加括號(hào)是不必要的。我這里這樣做,所以我可以在除法后面添加一行,使整個(gè)式子更加易讀和易理解。不這樣的話,我們會(huì)在新的一行得到語法錯(cuò)誤。我們幾乎完成了,現(xiàn)在我們只需要將x的均值平方和x的平方均值(mean(xs*xs))相減。全部代碼為:
def best_fit_slope(xs,ys):
m = (((mean(xs)*mean(ys)) - mean(xs*ys)) /
((mean(xs)**2) - mean(xs*xs)))
return m
好的,現(xiàn)在我們的完整腳本為:
from statistics import mean
import numpy as np
xs = np.array([1,2,3,4,5], dtype=np.float64)
ys = np.array([5,4,6,5,6], dtype=np.float64)
def best_fit_slope(xs,ys):
m = (((mean(xs)*mean(ys)) - mean(xs*ys)) /
((mean(xs)**2) - mean(xs**2)))
return m
m = best_fit_slope(xs,ys)
print(m)
# 0.3
下面干什么?我們需要計(jì)算縱截距b。我們會(huì)在下一個(gè)教程中處理它,并完成完整的最佳擬合直線計(jì)算。它比斜率更佳易于計(jì)算,嘗試編寫你自己的函數(shù)來計(jì)算它。如果你做到了,也不要跳過下一個(gè)教程,我們會(huì)做一些別的事情。
計(jì)算縱截距
歡迎閱讀第九篇教程。我們當(dāng)前正在為給定的數(shù)據(jù)集,使用 Python 計(jì)算回歸或者最佳擬合直線。之前,我們編寫了一個(gè)函數(shù)來計(jì)算斜率,現(xiàn)在我們需要計(jì)算縱截距。我們目前的代碼是:
from statistics import mean
import numpy as np
xs = np.array([1,2,3,4,5], dtype=np.float64)
ys = np.array([5,4,6,5,6], dtype=np.float64)
def best_fit_slope(xs,ys):
m = (((mean(xs)*mean(ys)) - mean(xs*ys)) /
((mean(xs)*mean(xs)) - mean(xs*xs)))
return m
m = best_fit_slope(xs,ys)
print(m)
請(qǐng)回憶,最佳擬合直線的縱截距是:
這個(gè)比斜率簡(jiǎn)單多了。我們可以將其寫到同一個(gè)函數(shù)來節(jié)省幾行代碼。我們將函數(shù)重命名為best_fit_slope_and_intercept。
下面,我們可以填充b = mean(ys) - (m*mean(xs)),并返回m, b:
def best_fit_slope_and_intercept(xs,ys):
m = (((mean(xs)*mean(ys)) - mean(xs*ys)) /
((mean(xs)*mean(xs)) - mean(xs*xs)))
b = mean(ys) - m*mean(xs)
return m, b
現(xiàn)在我們可以調(diào)用它:
best_fit_slope_and_intercept(xs,ys)
我們目前為止的代碼:
from statistics import mean
import numpy as np
xs = np.array([1,2,3,4,5], dtype=np.float64)
ys = np.array([5,4,6,5,6], dtype=np.float64)
def best_fit_slope_and_intercept(xs,ys):
m = (((mean(xs)*mean(ys)) - mean(xs*ys)) /
((mean(xs)*mean(xs)) - mean(xs*xs)))
b = mean(ys) - m*mean(xs)
return m, b
m, b = best_fit_slope_and_intercept(xs,ys)
print(m,b)
# 0.3, 4.3
現(xiàn)在我們僅僅需要為數(shù)據(jù)創(chuàng)建一條直線:
?
要記住y=mx+b,我們能夠?yàn)榇司帉懸粋€(gè)函數(shù),或者僅僅使用一行的for循環(huán)。
regression_line = [(m*x)+b for x in xs]
上面的一行for循環(huán)和這個(gè)相同:
regression_line = []
for x in xs:
regression_line.append((m*x)+b)
好的,讓我們收取我們的勞動(dòng)果實(shí)吧。添加下面的導(dǎo)入:
import matplotlib.pyplot as plt
from matplotlib import style
style.use('ggplot')
我們可以繪制圖像,并且不會(huì)特備難看?,F(xiàn)在:
plt.scatter(xs,ys,color='#003F72')
plt.plot(xs, regression_line)
plt.show()
首先我們繪制了現(xiàn)有數(shù)據(jù)的散點(diǎn)圖,之后我們繪制了我們的回歸直線,之后展示它。如果你不熟悉,可以查看 Matplotlib 教程集。
輸出:
?
所以,如何基礎(chǔ)這個(gè)模型來做一些實(shí)際的預(yù)測(cè)呢?很簡(jiǎn)單,你擁有了模型,只要填充x就行了。例如,讓我們預(yù)測(cè)一些點(diǎn):
predict_x = 7
我們輸入了數(shù)據(jù),也就是我們的特征。那么標(biāo)簽?zāi)兀?br />
predict_y = (m*predict_x)+b
print(predict_y)
# 6.4
我們也可以繪制它:
predict_x = 7
predict_y = (m*predict_x)+b
plt.scatter(xs,ys,color='#003F72',label='data')
plt.plot(xs, regression_line, label='regression line')
plt.legend(loc=4)
plt.show()
輸出:
?
我們現(xiàn)在知道了如何創(chuàng)建自己的模型,這很好,但是我們?nèi)耘f缺少了一些東西,我們的模型有多精確?這就是下一個(gè)教程的話題了。
R 平方和判定系數(shù)原理
歡迎閱讀第十篇教程。我們剛剛完成了線性模型的創(chuàng)建和處理,現(xiàn)在我們好奇接下來要干什么?,F(xiàn)在,我們可以輕易觀察數(shù),并決定線性回歸模型有多么準(zhǔn)確。但是,如果你的線性回歸模型是拿神經(jīng)網(wǎng)絡(luò)的 20 個(gè)層級(jí)做出來的呢?不僅僅是這樣,你的模型以步驟或者窗口工作,也就是一共 5 百萬個(gè)數(shù)據(jù)點(diǎn),一次只顯示 100 個(gè),會(huì)怎么樣?你需要一些自動(dòng)化的方式來判斷你的最佳擬合直線有多好。
回憶之前,我們展示幾個(gè)繪圖的時(shí)候,你已經(jīng)看到,最佳擬合直線好還是不好。像這樣:
?
與這個(gè)相比:
?
第二張圖片中,的確有最佳擬合直線,但是沒有人在意。即使是最佳擬合直線也是沒有用的。并且,我們想在花費(fèi)大量計(jì)算能力之前就知道它。
檢查誤差的標(biāo)準(zhǔn)方式就是使用平方誤差。你可能之前聽說過,這個(gè)方法叫做 R 平方或者判定系數(shù)。什么叫平方誤差呢?
?
回歸直線和數(shù)據(jù)的y值的距離,就叫做誤差,我們將其平方。直線的平方誤差是它們的平均或者和。我們簡(jiǎn)單求和吧。
我們實(shí)際上已經(jīng)解除了平方誤差假設(shè)。我們的最佳擬合直線方程,用于計(jì)算最佳擬合回歸直線,就是證明結(jié)果。其中回歸直線就是擁有最小平方誤差的直線(所以它才叫做最小二乘法)。你可以搜索“回歸證明”,或者“最佳擬合直線證明”來理解它。它很抑郁理解,但是需要代數(shù)變形能力來得出結(jié)果。
為啥是平方誤差?為什么不僅僅將其加起來?首先,我們想要一種方式,將誤差規(guī)范化為距離,所以誤差可能是 -5,但是,平方之后,它就是正數(shù)了。另一個(gè)原因是要進(jìn)一步懲罰離群點(diǎn)。進(jìn)一步的意思是,它影響誤差的程度更大。這就是人們所使用的標(biāo)準(zhǔn)方式。你也可以使用4, 6, 8的冪,或者其他。你也可以僅僅使用誤差的絕對(duì)值。如果你只有一個(gè)挑戰(zhàn),也許就是存在一些離群點(diǎn),但是你并不打算管它們,你就可以考慮使用絕對(duì)值。如果你比較在意離群點(diǎn),你就可以使用更高階的指數(shù)。我們會(huì)使用平方,因?yàn)檫@是大多數(shù)人所使用的。
好的,所以我們計(jì)算回歸直線的平方誤差,什么計(jì)算呢?這是什么意思?平方誤差完全和數(shù)據(jù)集相關(guān),所以我們不再需要?jiǎng)e的東西了。這就是 R 平方引入的時(shí)候了,也叫作判定系數(shù)。方程是:
?
y_hat = x * m + b
r_sq = 1 - np.sum((y - y_hat) ** 2) / np.sum((y - y.mean()) ** 2)
這個(gè)方程的的本質(zhì)就是,1 減去回歸直線的平方誤差,比上 y 平均直線的平方誤差。 y 平均直線就是數(shù)據(jù)集中所有 y 值的均值,如果你將其畫出來,它是一個(gè)水平的直線。所以,我們計(jì)算 y 平均直線,和回歸直線的平方誤差。這里的目標(biāo)是識(shí)別,與欠擬合的直線相比,數(shù)據(jù)特征的變化產(chǎn)生了多少誤差。
所以判定系數(shù)就是上面那個(gè)方程,如何判定它是好是壞?我們看到了它是 1 減去一些東西。通常,在數(shù)學(xué)中,你看到他的時(shí)候,它返回了一個(gè)百分比,它是 0 ~ 1 之間的數(shù)值。你認(rèn)為什么是好的 R 平方或者判定系數(shù)呢?讓我們假設(shè)這里的 R 平方是 0.8,它是好是壞呢?它比 0.3 是好還是壞?對(duì)于 0.8 的 R 平方,這就意味著回歸直線的平方誤差,比上 y 均值的平方誤差是 2 比 10。這就是說回歸直線的誤差非常小于 y 均值的誤差。聽起來不錯(cuò)。所以 0.8 非常好。
那么與判定系數(shù)的值 0.3 相比呢?這里,它意味著回歸直線的平方誤差,比上 y 均值的平方誤差是 7 比 10。其中 7 比 10 要壞于 2 比 10,7 和 2 都是回歸直線的平方誤差。因此,目標(biāo)是計(jì)算 R 平方值,或者叫做判定系數(shù),使其盡量接近 1。
編程計(jì)算 R 平方
歡迎閱讀第十一篇教程。既然我們知道了我們尋找的東西,讓我們實(shí)際在 Python 中計(jì)算它吧。第一步就是計(jì)算平方誤差。函數(shù)可能是這樣:
def squared_error(ys_orig,ys_line):
return sum((ys_line - ys_orig) * (ys_line - ys_orig))
使用上面的函數(shù),我們可以計(jì)算出任何實(shí)現(xiàn)到數(shù)據(jù)點(diǎn)的平方誤差。所以我們可以將這個(gè)語法用于回歸直線和 y 均值直線。也就是說,平方誤差只是判定系數(shù)的一部分,所以讓我們構(gòu)建那個(gè)函數(shù)吧。由于平方誤差函數(shù)只有一行,你可以選擇將其嵌入到判定系數(shù)函數(shù)中,但是平方誤差是你在這個(gè)函數(shù)之外計(jì)算的東西,所以我選擇將其單獨(dú)寫成一個(gè)函數(shù)。對(duì)于 R 平方:
def coefficient_of_determination(ys_orig,ys_line):
y_mean_line = [mean(ys_orig) for y in ys_orig]
squared_error_regr = squared_error(ys_orig, ys_line)
squared_error_y_mean = squared_error(ys_orig, y_mean_line)
return 1 - (squared_error_regr/squared_error_y_mean)
我們所做的是,計(jì)算 y 均值直線,使用單行的for循環(huán)(其實(shí)是不必要的)。之后我們計(jì)算了 y 均值的平方誤差,以及回歸直線的平方誤差,使用上面的函數(shù)?,F(xiàn)在,我們需要做的就是計(jì)算出 R 平方之,它僅僅是 1 減去回歸直線的平方誤差,除以 y 均值直線的平方誤差。我們返回該值,然后就完成了。組合起來并跳過繪圖部分,代碼為:
from statistics import mean
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import style
style.use('ggplot')
xs = np.array([1,2,3,4,5], dtype=np.float64)
ys = np.array([5,4,6,5,6], dtype=np.float64)
def best_fit_slope_and_intercept(xs,ys):
m = (((mean(xs)*mean(ys)) - mean(xs*ys)) /
((mean(xs)*mean(xs)) - mean(xs*xs)))
b = mean(ys) - m*mean(xs)
return m, b
def squared_error(ys_orig,ys_line):
return sum((ys_line - ys_orig) * (ys_line - ys_orig))
def coefficient_of_determination(ys_orig,ys_line):
y_mean_line = [mean(ys_orig) for y in ys_orig]
squared_error_regr = squared_error(ys_orig, ys_line)
squared_error_y_mean = squared_error(ys_orig, y_mean_line)
return 1 - (squared_error_regr/squared_error_y_mean)
m, b = best_fit_slope_and_intercept(xs,ys)
regression_line = [(m*x)+b for x in xs]
r_squared = coefficient_of_determination(ys,regression_line)
print(r_squared)
# 0.321428571429
##plt.scatter(xs,ys,color='#003F72',label='data')
##plt.plot(xs, regression_line, label='regression line')
##plt.legend(loc=4)
##plt.show()
這是個(gè)很低的值,所以根據(jù)這個(gè)度量,我們的最佳擬合直線并不是很好。這里的 R 平方是個(gè)很好的度量手段嗎?可能取決于我們的目標(biāo)。多數(shù)情況下,如果我們關(guān)心準(zhǔn)確預(yù)測(cè)未來的值,R 平方的確很有用。如果你對(duì)預(yù)測(cè)動(dòng)機(jī)或者趨勢(shì)感興趣,我們的最佳擬合直線實(shí)際上已經(jīng)很好了。R 平方不應(yīng)該如此重要。看一看我們實(shí)際的數(shù)據(jù)集,我們被一個(gè)較低的數(shù)值卡住了。值與值之間的變化在某些點(diǎn)上是 20% ~ 50%,這已經(jīng)非常高了。我們完全不應(yīng)該感到意外,使用這個(gè)簡(jiǎn)單的數(shù)據(jù)集,我們的最佳擬合直線并不能描述真實(shí)數(shù)據(jù)。
但是,我們剛才說的是一個(gè)假設(shè)。雖然我們邏輯上統(tǒng)一這個(gè)假設(shè),我們需要提出一個(gè)新的方法,來驗(yàn)證假設(shè)。到目前為止的算法非常基礎(chǔ),我們現(xiàn)在只能做很少的事情,所以沒有什么空間來改進(jìn)誤差了,但是之后,你會(huì)在空間之上發(fā)現(xiàn)空間。不僅僅要考慮算法本身的層次空間,還有由很多算法層次組合而成的算法。其中,我們需要測(cè)試它們來確保我們的假設(shè),關(guān)于算法是干什么用的,是正確的??紤]把操作組成成函數(shù)由多么簡(jiǎn)單,之后,從這里開始,將整個(gè)驗(yàn)證分解成數(shù)千行代碼。
我們?cè)谙乱黄坛趟龅氖?,?gòu)建一個(gè)相對(duì)簡(jiǎn)單的數(shù)據(jù)集生成器,根據(jù)我們的參數(shù)來生成數(shù)據(jù)。我們可以使用它來按照意愿操作數(shù)據(jù),之后對(duì)這些數(shù)據(jù)集測(cè)試我們的算法,根據(jù)我們的假設(shè)修改參數(shù),應(yīng)該會(huì)產(chǎn)生一些影響。我們之后可以將我們的假設(shè)和真實(shí)情況比較,并希望他們匹配。這里的例子中,假設(shè)是我們正確編寫這些算法,并且判定系數(shù)低的原因是,y 值的方差太大了。我們會(huì)在下一個(gè)教程中驗(yàn)證這個(gè)假設(shè)。
為測(cè)試創(chuàng)建樣例數(shù)據(jù)集
歡迎閱讀第十二篇教程。我們已經(jīng)了解了回歸,甚至編寫了我們自己的簡(jiǎn)單線性回歸算法。并且,我們也構(gòu)建了判定系數(shù)算法來檢查最佳擬合直線的準(zhǔn)確度和可靠性。我們之前討論和展示過,最佳擬合直線可能不是最好的擬合,也解釋了為什么我們的示例方向上是正確的,即使并不準(zhǔn)確。但是現(xiàn)在,我們使用兩個(gè)頂級(jí)算法,它們由一些小型算法組成。隨著我們繼續(xù)構(gòu)造這種算法層次,如果它們之中有個(gè)小錯(cuò)誤,我們就會(huì)遇到麻煩,所以我們打算驗(yàn)證我們的假設(shè)。
在編程的世界中,系統(tǒng)化的程序測(cè)試通常叫做“單元測(cè)試”。這就是大型程序構(gòu)建的方式,每個(gè)小型的子系統(tǒng)都不斷檢查。隨著大型程序的升級(jí)和更新,可以輕易移除一些和之前系統(tǒng)沖突的工具。使用機(jī)器學(xué)習(xí),這也是個(gè)問題,但是我們的主要關(guān)注點(diǎn)僅僅是測(cè)試我們的假設(shè)。最后,你應(yīng)該足夠聰明,可以為你的整個(gè)機(jī)器學(xué)習(xí)系統(tǒng)創(chuàng)建單元測(cè)試,但是目前為止,我們需要盡可能簡(jiǎn)單。
我們的假設(shè)是,我們創(chuàng)建了最賤he直線,之后使用判定系數(shù)法來測(cè)量。我們知道(數(shù)學(xué)上),R 平方的值越低,最佳擬合直線就越不好,并且越高(接近 1)就越好。我們的假設(shè)是,我們構(gòu)建了一個(gè)這樣工作的系統(tǒng),我們的系統(tǒng)有許多部分,即使是一個(gè)小的操作錯(cuò)誤都會(huì)產(chǎn)生很大的麻煩。我們?nèi)绾螠y(cè)試算法的行為,保證任何東西都預(yù)期工作呢?
這里的理念是創(chuàng)建一個(gè)樣例數(shù)據(jù)集,由我們定義,如果我們有一個(gè)正相關(guān)的數(shù)據(jù)集,相關(guān)性非常強(qiáng),如果相關(guān)性很弱的話,點(diǎn)也不是很緊密。我們用眼睛很容易評(píng)測(cè)這個(gè)直線,但是機(jī)器應(yīng)該做得更好。讓我們構(gòu)建一個(gè)系統(tǒng),生成示例數(shù)據(jù),我們可以調(diào)整這些參數(shù)。
最開始,我們構(gòu)建一個(gè)框架函數(shù),模擬我們的最終目標(biāo):
def create_dataset(hm,variance,step=2,correlation=False):
return np.array(xs, dtype=np.float64),np.array(ys,dtype=np.float64)
我們查看函數(shù)的開頭,它接受下列參數(shù):
hm(how much):這是生成多少個(gè)數(shù)據(jù)點(diǎn)。例如我們可以選擇 10,或者一千萬。
variance:決定每個(gè)數(shù)據(jù)點(diǎn)和之前的數(shù)據(jù)點(diǎn)相比,有多大變化。變化越大,就越不緊密。
step:每個(gè)點(diǎn)距離均值有多遠(yuǎn),默認(rèn)為 2。
correlation:可以為False、pos或者neg,決定不相關(guān)、正相關(guān)和負(fù)相關(guān)。
要注意,我們也導(dǎo)入了random,這會(huì)幫助我們生成(偽)隨機(jī)數(shù)據(jù)集。
現(xiàn)在我們要開始填充函數(shù)了。
def create_dataset(hm,variance,step=2,correlation=False):
val = 1
ys = []
for i in range(hm):
y = val + random.randrange(-variance,variance)
ys.append(y)
非常簡(jiǎn)單,我們僅僅使用hm變量,迭代我們所選的范圍,將當(dāng)前值加上一個(gè)負(fù)差值到證差值的隨機(jī)范圍。這會(huì)產(chǎn)生數(shù)據(jù),但是如果我們想要的話,它沒有相關(guān)性。讓我們這樣:
def create_dataset(hm,variance,step=2,correlation=False):
val = 1
ys = []
for i in range(hm):
y = val + random.randrange(-variance,variance)
ys.append(y)
if correlation and correlation == 'pos':
val+=step
elif correlation and correlation == 'neg':
val-=step
非常棒了,現(xiàn)在我們定義好了 y 值。下面,讓我們創(chuàng)建 x,它更簡(jiǎn)單,只是返回所有東西。
def create_dataset(hm,variance,step=2,correlation=False):
val = 1
ys = []
for i in range(hm):
y = val + random.randrange(-variance,variance)
ys.append(y)
if correlation and correlation == 'pos':
val+=step
elif correlation and correlation == 'neg':
val-=step
xs = [i for i in range(len(ys))]
return np.array(xs, dtype=np.float64),np.array(ys,dtype=np.float64)
我們準(zhǔn)備好了。為了創(chuàng)建樣例數(shù)據(jù)集,我們所需的就是:
xs, ys = create_dataset(40,40,2,correlation='pos')
讓我們將之前線性回歸教程的代碼放到一起:
from statistics import mean
import numpy as np
import random
import matplotlib.pyplot as plt
from matplotlib import style
style.use('ggplot')
def create_dataset(hm,variance,step=2,correlation=False):
val = 1
ys = []
for i in range(hm):
y = val + random.randrange(-variance,variance)
ys.append(y)
if correlation and correlation == 'pos':
val+=step
elif correlation and correlation == 'neg':
val-=step
xs = [i for i in range(len(ys))]
return np.array(xs, dtype=np.float64),np.array(ys,dtype=np.float64)
def best_fit_slope_and_intercept(xs,ys):
m = (((mean(xs)*mean(ys)) - mean(xs*ys)) /
((mean(xs)*mean(xs)) - mean(xs*xs)))
b = mean(ys) - m*mean(xs)
return m, b
def coefficient_of_determination(ys_orig,ys_line):
y_mean_line = [mean(ys_orig) for y in ys_orig]
squared_error_regr = sum((ys_line - ys_orig) * (ys_line - ys_orig))
squared_error_y_mean = sum((y_mean_line - ys_orig) * (y_mean_line - ys_orig))
print(squared_error_regr)
print(squared_error_y_mean)
r_squared = 1 - (squared_error_regr/squared_error_y_mean)
return r_squared
xs, ys = create_dataset(40,40,2,correlation='pos')
m, b = best_fit_slope_and_intercept(xs,ys)
regression_line = [(m*x)+b for x in xs]
r_squared = coefficient_of_determination(ys,regression_line)
print(r_squared)
plt.scatter(xs,ys,color='#003F72', label = 'data')
plt.plot(xs, regression_line, label = 'regression line')
plt.legend(loc=4)
plt.show()
執(zhí)行代碼,你會(huì)看到:
?
判定系數(shù)是 0.516508576011(要注意你的結(jié)果不會(huì)相同,因?yàn)槲覀兪褂昧穗S機(jī)數(shù)范圍)。
不錯(cuò),所以我們的假設(shè)是,如果我們生成一個(gè)更加緊密相關(guān)的數(shù)據(jù)集,我們的 R 平方或判定系數(shù)應(yīng)該更好。如何實(shí)現(xiàn)它呢?很簡(jiǎn)單,把范圍調(diào)低。
xs, ys = create_dataset(40,10,2,correlation='pos')
?
?
現(xiàn)在我們的 R 平方值為 0.939865240568,非常不錯(cuò),就像預(yù)期一樣。讓我們測(cè)試負(fù)相關(guān):
xs, ys = create_dataset(40,10,2,correlation='neg')
?
R 平方值是 0.930242442156,跟之前一樣好,由于它們參數(shù)相同,只是方向不同。
這里,我們的假設(shè)證實(shí)了:變化越小 R 值和判定系數(shù)越高,變化越大 R 值越低。如果是不相關(guān)呢?應(yīng)該很低,接近于 0,除非我們的隨機(jī)數(shù)排列實(shí)際上有相關(guān)性。讓我們測(cè)試:
xs, ys = create_dataset(40,10,2,correlation=False)
?
判定系數(shù)為 0.0152650900427。
現(xiàn)在為止,我覺得我們應(yīng)該感到自信,因?yàn)槭虑槎挤衔覀兊念A(yù)期。
既然我們已經(jīng)對(duì)簡(jiǎn)單的線性回歸很熟悉了,下個(gè)教程中我們開始講解分類。
評(píng)論
查看更多