以預測股票漲跌案例入門基於SVM的機器學習

  • 2019 年 10 月 3 日
  • 筆記

    SVM是Support Vector Machine的縮寫,中文叫支援向量機,通過它可以對樣本數據進行分類。以股票為例,SVM能根據若干特徵樣本數據,把待預測的目標結果劃分成“漲”和”跌”兩種,從而實現預測股票漲跌的效果。

1 通過簡單案例了解SVM的分類作用

    在Sklearn庫里,封裝了SVM分類的相關方法,也就是說,我們無需了解其中複雜的演算法,即可用它實現基於SVM的分類。通過如下SimpleSVMDemo.py案例,我們來看下通過SVM庫實現分類的做法,以及相關方法的調用方式。    

1    #!/usr/bin/env python  2    #coding=utf-8  3    import numpy as np  4    import matplotlib.pyplot as plt  5    from sklearn import svm  6    #給出平面上的若干點  7    points = np.r_[[[-1,1],[1.5,1.5],[1.8,0.2],[0.8,0.7],[2.2,2.8],[2.5,3.5],[4,2]]]  8    #按0和1標記成兩類  9    typeName = [0,0,0,0,1,1,1]

    在第5行里,我們引入了基於SVM的庫。在第7行,我們定義了若干個點,並在第9行把這些點分成了兩類,比如[-1,1]點是第一類,而[4,2]是第二類。

    這裡請注意,在第7行定義點的時候,是通過np.r_方法,把數據轉換成“列矩陣”,這樣做的目的是讓數據結構滿足fit方法的要求。     

10	#建立模型  11	svmTool = svm.SVC(kernel='linear')  12	svmTool.fit(points,typeName)  #傳入參數  13	#確立分類的直線  14	sample = svmTool.coef_[0] #係數  15	slope = -sample[0]/sample[1]  #斜率  16	lineX = np.arange(-2,5,1)#獲取-2到5,間距是1的若干數據  17	lineY = slope*lineX-(svmTool.intercept_[0])/sample[1]  

  在第11行里,我們創建了基於SVM的對象,並指定該SVM模型採用比較常用的“線性核”來實現分類操作。

      在第14行,通過fit訓練樣本。這裡fit方法和之前基於線性回歸案例中的fit方法是一樣的,只不過這裡是基於線性核的相關演算法,而之前是基於線性回歸的相關演算法(比如最小二乘法)。訓練完成後,通過第14行和第15行的程式碼,我們得到了能分隔兩類樣本的直線,包括直線的斜率和截距,並通過第16行和第17行的程式碼設置了分隔線的若干個點。    

18	#畫出劃分直線  19	plt.plot(lineX,lineY,color='blue',label='Classified Line')  20	plt.legend(loc='best') #繪製圖例  21	plt.scatter(points[:,0],points[:,1],c='R')  22	plt.show()  

  計算完成後,我們通過第19行的plot方法繪製了分隔線,並在第21行通過scatter方法繪製所有的樣本點。由於points是“列矩陣”的數據結構,所以是用points[:,0]來獲取繪製點的 x坐標,用points[:,1]來獲取y坐標,最後是通過第22行的show方法繪製圖形。運行上述程式碼,我們能看到如下圖13.8的效果,從中我們能看到,藍色的邊界線能有效地分隔兩類樣本。

    

    從這個例子中我們能看到,SVM的作用是,根據樣本,訓練出能劃分不同種類數據的邊界線,由此實現“分類”的效果。而且,在根據訓練樣本確定好邊界線的參數後,還能根據其它沒有明確種類樣本,計算出它的種類,以此實現“預測”效果。 

2 數據標準化處理

    標準化(normalization)處理是將特徵樣本按一定演算法進行縮放,讓它們落在某個範圍比較小的區間,同時去掉單位限制,讓樣本數據轉換成無量綱的純數值。

    在用機器學習方法進行訓練時,一般需要進行標準化處理,原因是Sklearn等庫封裝的一些機器學習演算法對樣本有一定的要求,如果有些特徵值的數量級偏離大多數特徵值的數量級,或者有特徵值偏離正態分布,那麼預測結果會不準確。

    需要說明的是,雖然在訓練前對樣本進行了標準化處理,改變了樣本值,但由於在標準化的過程中是用同一個演算法對全部樣本進行轉換,屬於“數據優化”,不會對後繼的訓練起到不好的作用。

    這裡我們是通過sklearn庫提供的preprocessing.scale方法實現標準化,該方法是讓特徵值減去平均值然後除以標準差。通過如下ScaleDemo.py案例,我們實際用下preprocessing.scale方法。     

1	#!/usr/bin/env python  2	#coding=utf-8  3	from sklearn import preprocessing  4	import numpy as np  5  6	origVal = np.array([[10,5,3],  7	                   [8,6,12],  8	                   [14,7,15]])  9	#計算均值  10	avgOrig = origVal.mean(axis=0)  11	#計算標準差  12	stdOrig=origVal.std(axis=0)  13	#減去均值,除以標準差  14	print((origVal-avgOrig)/stdOrig)  15	scaledVal=preprocessing.scale(origVal)  16	#直接輸出preprocessing.scale後的結果  17	print(scaledVal)  

  在第6行里,我們初始化了一個長寬各為3的矩陣,在第10行,通過mean方法計算了該矩陣的均值,在第12行則通過std方法計算標準差。

      第14行是用原始值減去均值,再除以標準差,在第17行,是直接輸出preprocessing.scale的結果。第14行和第17行的輸出結果相同,均是下值,從中我們驗證了標準化的具體做法。    

1	[[-0.26726124 -1.22474487 -1.37281295]  2	 [-1.06904497  0.          0.39223227]  3	 [ 1.33630621  1.22474487  0.98058068]]  

   

3 預測股票漲跌

    在之前的案例中,我們用基於SVM的方法,通過一維直線來分類二維的點。據此可以進一步推論:通過基於SVM的方法,我們還可以分類具有多個特徵值的樣本。

    比如可以通過開盤價、收盤價、最高價、最低價和成交量等特徵值,用SVM的演算法訓練出這些特徵值和股票“漲“和“跌“的關係,即通過特徵值劃分指定股票“漲”和“跌”的邊界,這樣的話,一旦輸入其它的股票特徵數據,即可預測出對應的漲跌情況。在如下的PredictStockBySVM.py案例中,我們給出了基於SVM預測股票漲跌的功能。     

1	#!/usr/bin/env python  2	#coding=utf-8  3	import pandas as pd  4	from sklearn import svm,preprocessing  5	import matplotlib.pyplot as plt  6	origDf=pd.read_csv('D:/stockData/ch13/6035052018-09-012019-05-31.csv',encoding='gbk')  7	df=origDf[['Close', 'Low','Open' ,'Vol','Date']]  8	#diff列表示本日和上日收盤價的差  9	df['diff'] = df["Close"]-df["Close"].shift(1)  10	df['diff'].fillna(0, inplace = True)  11	#up列表示本日是否上漲,1表示漲,0表示跌  12	df['up'] = df['diff']  13	df['up'][df['diff']>0] = 1  14	df['up'][df['diff']<=0] = 0  15	#預測值暫且初始化為0  16	df['predictForUp'] = 0  

  第6行里,我們從指定文件讀取了包含股票資訊的csv文件,該csv格式的文件其實是從網路數據介面獲取得到的,具體做法可以參考前面博文。

    從第9行里,我們設置了df的diff列為本日收盤價和前日收盤價的差值,通過第12行到第14行的程式碼,我們設置了up列的值,具體是,如果當日股票上漲,即本日收盤價大於前日收盤價,則up值是1,反之如果當日股票下跌,up值則為0。

    在第16行里,我們在df對象里新建了表示預測結果的predictForUp列,該列的值暫且都設置為0,在後繼的程式碼里,將根據預測結果填充這列的值。    

17	#目標值是真實的漲跌情況  18	target = df['up']  19	length=len(df)  20	trainNum=int(length*0.8)  21	predictNum=length-trainNum  22	#選擇指定列作為特徵列  23	feature=df[['Close', 'High', 'Low','Open' ,'Volume']]  24	#標準化處理特徵值  25	feature=preprocessing.scale(feature)  

  在第18行里,我們設置訓練目標值是表示漲跌情況的up列,在第20行,設置了訓練集的數量是總量的80%,在第23行則設置了訓練的特徵值,請注意這裡去掉了日期這個不相關的列,而且,在第25行,對特徵值進行了標準化處理。    

26	#訓練集的特徵值和目標值  27	featureTrain=feature[1:trainNum-1]  28	targetTrain=target[1:trainNum-1]  29	svmTool = svm.SVC(kernel='liner')  30	svmTool.fit(featureTrain,targetTrain)  

  在第27行和第28行里,我們通過截取指定行的方式,得到了特徵值和目標值的訓練集,在第26行里,以線性核的方式創建了SVM分類器對象svmTool。

     在第30行里,通過fit方法,用特徵值和目標值的訓練集訓練svmTool分類對象。從上文里我們已經看到,訓練所用的特徵值是開盤收盤價、最高最低價和成交量,訓練所用的目標值是描述漲跌情況的up列。在訓練完成後,svmTool對象中就包含了能劃分股票漲跌的相關參數。

31	predictedIndex=trainNum  32	#逐行預測測試集  33	while predictedIndex<length:  34	    testFeature=feature[predictedIndex:predictedIndex+1]  35	    predictForUp=svmTool.predict(testFeature)  36	    df.ix[predictedIndex,'predictForUp']=predictForUp  37	    predictedIndex = predictedIndex+1  

    在第33行的while循環里,我們通過predictedIndex索引值,依次遍歷測試集。

    在遍歷過程中,通過第35行的predict方法,用訓練好的svmTool分類器,逐行預測測試集中的股票漲跌情況,並在第36行里,把預測結果設置到df對象的predictForUp列中。      

38	#該對象只包含預測數據,即只包含測試集  39	dfWithPredicted = df[trainNum:length]  40	#開始繪圖,創建兩個子圖  41	figure = plt.figure()  42	#創建子圖  43	(axClose, axUpOrDown) = figure.subplots(2, sharex=True)  44	dfWithPredicted['Close'].plot(ax=axClose)  45	dfWithPredicted['predictForUp'].plot(ax=axUpOrDown,color="red", label='Predicted Data')  46	dfWithPredicted['up'].plot(ax=axUpOrDown,color="blue",label='Real Data')  47	plt.legend(loc='best') #繪製圖例  48	#設置x軸坐標標籤和旋轉角度  49	major_index=dfWithPredicted.index[dfWithPredicted.index%2==0]  50	major_xtics=dfWithPredicted['Date'][dfWithPredicted.index%2==0]  51	plt.xticks(major_index,major_xtics)  52	plt.setp(plt.gca().get_xticklabels(), rotation=30)  53	plt.title("通過SVM預測603505的漲跌情況")  54	plt.rcParams['font.sans-serif']=['SimHei']  55	plt.show()  

  由於在之前的程式碼里,我們只設置測試集的predictForUp列,並沒有設置訓練集的該列數據,所以在第39行里,用切片的手段,把測試集數據放置到dfWithPredicted對象中,請注意這裡切片的起始和結束值是測試集的起始和結束索引值。至此完成了數據準備工作,在之後的程式碼里,我們將用matplotlib庫開始繪圖。

    在第43行里,我們通過subplots方法設置了兩個子圖,並通過sharex=True讓這兩個子圖的x軸具有相同的刻度和標籤。在第44行程式碼里,在axClose子圖中,我們用plot方法繪製了收盤價的走勢。在第45行程式碼里,在axUpOrDown子圖中,我們繪製了預測到的漲跌情況,而在第46行里,還是在axUpOrDown子圖裡,繪製了這些天的股票真實的漲跌情況。

    在第49行到第52行的程式碼里,我們設置了x標籤的文字以及旋轉角度,這樣做的目的是讓標籤文字看上去不至於太密集。在第53行里,我們設置了中文標題,由於要顯示中文,所以需要第54行的程式碼,最後在55行通過show方法展示了圖片。運行上述程式碼,能看到如下圖所示的效果。

    

    其中上圖展示了收盤價,下圖的藍色線條表示真實的漲跌情況,0表示跌,1表示上漲,而紅色則表示預測後的結果。

4 結論

     對比一下,雖有偏差,但大體相符。綜上所述,本案例是數學角度,演示了通過SVM分類的做法,包括如果劃分特徵值和目標值,如何對樣本數據進行標準化處理,如何用訓練數據訓練SVM,還有如何用訓練後的結果預測分類結果。 

5 總結和版權說明

    本文是給程式設計師加財商系列,之前還有兩篇博文

 

    
    本文的內容即將出書,在出版的書里,是用股票案例和大家講述Python入門時的知識點,敬請期待
    

    有不少網友轉載和想要轉載我的博文,本人感到十分榮幸,這也是本人不斷寫博文的動力。關於本文的版權有如下統一的說明,抱歉就不逐一回復了。

    1 本文可轉載,無需告知,轉載時請用鏈接的方式,給出原文出處,別簡單地通過文本方式給出,同時寫明原作者是hsm_computer。

    2 在轉載時,請原文轉載 ,謝絕洗稿。否則本人保留追究法律責任的權利。