情感数据对LSTM股票预测模型的影响研究
情感數據對LSTM股票預測模型的影響研究
作者:丁紀翔
發布時間:06/28/2021
摘要:探究了情感結構化特征數據在LSTM股票預測模型中的影響。利用Pandas對所給數據進行預處理(數據載入、清洗與準備、規整、時間序列處理、數據聚合等)。[1] 借助NLTK和LM金融詞庫,對非結構化文本信息進行情感分析,并將所得結構化數據融入純技術指標的股票數據中。分析各股票指標的相關性,實現數據降維。基于Keras的以MSE為誤差評價方法的LSTM模型,實現對股票收盤價Close的預測。最終得出當訓練樣本充足時,融入了情感特征數據,使得預測精度適當增加的結論。
實驗說明:
設計一個預測股票價格的方法,并用實例證明此方法的有效性。
所給的數據,要求全部都要使用,注意數據需清洗、特征綜合使用,可自己額外補充資源或數據。
提供的數據說明:
全標題
a) 這是股票平臺上發布的對各公司的分析文章
b) 標題:文章的標題
c) 字段1_鏈接_鏈接:原文章所在的URL
d) ABOUT:文章針對的公司,都為縮寫形式,多個公司以逗號隔開
e) TIME:文章發布的時間
f) AUTHOR:作者
g) COMMENTS:采集時,文章的被評論次數
摘要
a) 這是股票平臺上發布的對各公司的分析文章的摘要部分,和“全標題”中的內容對應
b) 標題:文章的標題
c) 字段2:文章發布的時間
d) 字段5:文章針對的公司及提及的公司;
? i. About為針對公司,都提取縮寫的大寫模型,多個公司以逗號隔開
? ii. include為提及的其它公司,都提取縮寫的大寫模型,多個公司以逗號隔開
e) 字段1:摘要的全文字內容
回帖
a) 這是網友在各文章下的回復內容
b) Title:各文章的標題;空標題的,用最靠近的有內容的下方標題
c) Content:回復的全文字內容
論壇
a) 這是網友在各公司的論壇頁面下,對之進行評論的發帖內容
b) 字段1:作者
c) 字段2:發帖日期
d) 字段3:帖子內容
e) 字段4_鏈接:具體的各公司的頁面URL
股票價格
a) 為各公司工作日股票的價格
b) PERMNO:公司編號
c) Date:日期
d) TICKER:公司簡寫
e) COMNAM:公司全寫
f) BIDLO:最低價
g) ASKHI:最高價
h) PRC: 收盤價
i) VOL:成交量
j) OPENPRC: 開盤價
文章目錄
- 情感數據對LSTM股票預測模型的影響研究
- 1 LSTM
- 1.1 LSTM是什么?
- 1.2 為什么決定使用LSTM?
- 2 深度學習名詞概念解釋
- 2.1 為什么要使用多于一個epoch?
- 2.2 Batch 和 Batch_Size
- 2.3 Iterations
- 2.4 為什么不要shuffle?
- 3 實驗過程
- 3.1 庫導入
- 3.2 pandas核心設置
- 3.3 數據載入、數據清洗與準備、數據規整、時間序列處理
- 3.3.1 股票價格.csv
- 3.3.2 論壇.csv
- 3.3.3 全標題.xlsx
- 3.3.4 摘要.xlsx
- 3.3.5 回帖
- 3.4 情感分析
- 3.4.1 情感分析思路
- 3.4.2 詞庫導入和添加停用詞
- 3.4.3 函數定義
- 3.4.4 情感分析處理
- 3.4.5 情感特征數據聚合
- 3.5 \* 融入情感數據的股票指標相關性分析
- 3.5.1 數據聯合
- 3.5.2 pairplot繪圖
- 3.5.3 股票指標相關性分析
- 3.6 LSTM預測融合情感特征的股票數據
- 3.6.1 時間序列轉有監督函數定義
- 3.6.2 融合情感的股票數據歸一化
- 3.6.3 時間序列構建有監督數據集
- 3.6.4 訓練集驗證集劃分
- 3.6.5 基于Keras的LSTM模型搭建
- 3.6.5 (一)、重塑LSTM的輸入X
- 3.6.5 (二)、搭建LSTM模型并繪制損失圖
- 3.6.6 預測結果并反歸一化
- 3.6.7 模型評估
- 3.7 對比實驗:預測純技術指標的股票數據
- 3.7.1 對比實驗流程(通用函數構造)
- 3.7.2 對比實驗結果分析
- 3.7.3 對比實驗結論
- 3.8 補充對比實驗:補充AAPL股票技術指標樣本量進行預測
- 3.8.1 數據獲取
- 3.8.2 數據處理
- 3.8.3 預測分析
- 3.8.4 結果分析
- 3.9 2018全年含情感特征的股票數據預測實驗
- 3.9.1 情感特征數據聚合
- 3.9.2 預測分析
- 3.9.3 結果分析
- 4. 結論與總結
- 5. 參考文獻
核心思想:使用LSTM模型解決股票數據的時間序列預測問題和使用NLTK庫對文本情感進行分析。
根本觀點:歷史會不斷重演。本次作業均基于如下假設,股票規律并不是完全隨機的,而是受人類心理學中某些規律的制約,在面對相似的情境時,會根據以往的經驗和規律作出相似的反應。因此,可以根據歷史資料的數據來預測未來股票的波動趨勢。在股票的技術指標中,收盤價是一天結束時的價格,又是第二天的開盤價,聯系前后兩天,因此最為重要。[2]
影響因素:影響股票價格的因素除了基本的股票技術指標外,股票價格還和股民的情緒和相關股票分析文章的情感密切相關。
分析方法:將股票的技術指標和股民大眾的情感評價相結合[3],選擇AAPL個股,對股票價格,即收盤價進行預測。分別對只含有技術指標和含有技術指標和情感評價的樣本進行LSTM建模,使用MSE(均方誤差)作為損失函數,對二者預測結果進行評價。
1 LSTM
1.1 LSTM是什么?
LSTM Networks(Long Short-Term Memory)- Hochreiter 1997,長短期記憶神經網絡,是一種特殊的RNN,能夠學習長的依賴關系,記住較長的歷史信息。
1.2 為什么決定使用LSTM?
Deep Neural Networks (DNN),深度神經網絡,有若干輸入和一個輸出,在輸出和輸入間學習得到一個線性關系,接著通過一個神經元激活函數得到結果1或-1. 但DNN不能較好地處理時間序列數據。Recurrent Neural Networks (RNN),循環神經網絡,可以更好地處理序列信息,但其缺點是不能記憶較長時期的時間序列,而且 Standard RNN Shortcomings 難以訓練,給定初值條件下,收斂難度大。
LSTM解決了RNN的缺陷。LSTM相較于RNN模型增加了Forget Gate Layer(遺忘門),可以對上一個節點傳進的輸入進行選擇性忘記。接著,選擇需要記憶的重要輸入信息。也就是“忘記不重要的,記住重要的”。這樣,就解決了RNN在長序列訓練過程中的梯度消失和梯度爆炸問題,在長序列訓練中有更佳的表現。因此,我選用LSTM作為股票時間序列數據的訓練模型。
2 深度學習名詞概念解釋
| Epoch | 使用訓練集的全部數據對模型進行一次完整的訓練,被稱之為“一代訓練”。包括一次正向傳播和一次反向傳播 |
| Batch | 使用訓練集中的一小部分樣本對模型權重進行一次反向傳播的參數更新,這一小部分樣本被稱為“一批數據” |
| Iteration | 使用一個Batch數據對模型進行一次參數更新的過程,被稱之為“一次迭代 |
[Source1] https://www.jianshu.com/p/22c50ded4cf7?from=groupmessage
2.1 為什么要使用多于一個epoch?
只傳遞一次完整數據集是不夠的,需要在神經網絡中傳遞多次。隨著epoch數量的增加,神經網絡中的權重更新次數也在增加,這就導致了擬合曲線從欠擬合變為過擬合。
每次epoch之后,需要對總樣本shuffle,再進入下一輪訓練。(本次實驗不用shuffle)
對不同數據集,epoch個數不同。
2.2 Batch 和 Batch_Size
目前絕大部分深度學習框架使用Mini-batch Gradient Decent 小批梯度下降,把數據分為若干批(Batch),每批有Batch_Size個數據,按批更新權重,一個Batch中的一組數據共同決定本次梯度的下降方向。
NumberofBatches=TrainingSetSizeBatchSizeNumber of Batches = \frac{Training Set Size}{Batch Size} NumberofBatches=BatchSizeTrainingSetSize?
小批梯度下降克服了在數據量較大的情況下時,Batch Gradient Decent 的計算開銷大、速度慢 和 Stochastic Gradient Decent 的隨機性、收斂效果不佳的缺點。
[Source2] https://blog.csdn.net/dancing_power/article/details/97015723
2.3 Iterations
一次iteration進行一次前向傳播和反向傳播。前向傳播,基于屬性X,得到預測結果y。反向傳播根據給定的損失函數,求解參數(權重)。
NumbersofIterations=NumberofBatchedNumbers of Iterations = Number of Batched NumbersofIterations=NumberofBatched
2.4 為什么不要shuffle?
避免數據投入的順序對網絡訓練造成影響,增加訓練的隨機性,提高網絡的泛化性能。
但是針對本次股票價格的預測,使用LSTM模型,考慮時間因素,因此,需要設置shuffle=False,按時序順序依次使用Batch更新參數。
3 實驗過程
以下實驗均基于對Apple, Inc.(AAPL)蘋果公司的股票進行預測分析。
CORPORATIONABBR = 'AAPL'
3.1 庫導入
# 數據分析的核心庫 import numpy as np import pandas as pd from matplotlib import pyplot as plt # 時間序列處理 from datetime import datetime from dateutil.parser import parse as dt_parse # 正則庫 import re # os庫 from os import listdir # NLTK自然語言處理庫 import nltk from nltk.corpus import stopwords # seaborn成對圖矩陣生成 from seaborn import pairplot # sklearn庫的歸一化、訓練集測試集劃分 from sklearn.preprocessing import MinMaxScaler from sklearn.model_selection import train_test_split # Keras LSTM from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout # sklearn MSE from sklearn.metrics import mean_squared_error3.2 pandas核心設置
# 設置pandas的最大顯示行數、列數和輸出寬度 pd.set_option('display.max_rows', 6) pd.set_option('display.max_columns', 999) pd.set_option('display.max_colwidth', 50)3.3 數據載入、數據清洗與準備、數據規整、時間序列處理
3.3.1 股票價格.csv
sharePrices = pd.read_csv('股票價格.csv') sharePrices| 10026 | 20180702 | JJSF | J & J SNACK FOODS CORP | 150.70000 | 153.27499 | 152.92000 | 100388.0 | 152.17999 |
| 10026 | 20180703 | JJSF | J & J SNACK FOODS CORP | 151.35001 | 153.73000 | 153.32001 | 55547.0 | 153.67000 |
| 10026 | 20180705 | JJSF | J & J SNACK FOODS CORP | 152.46001 | 156.00000 | 155.81000 | 199370.0 | 153.95000 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 93436 | 20181227 | TSLA | TESLA INC | 301.50000 | 322.17169 | 316.13000 | 8575133.0 | 319.84000 |
| 93436 | 20181228 | TSLA | TESLA INC | 318.41000 | 336.23999 | 333.87000 | 9938992.0 | 323.10001 |
| 93436 | 20181231 | TSLA | TESLA INC | 325.26001 | 339.20999 | 332.79999 | 6302338.0 | 337.79001 |
941518 rows × 9 columns
索引過濾:索引過濾出TICKER(公司簡寫)為AAPL的數據行。
sharePricesAAPL = sharePrices[sharePrices['TICKER']==CORPORATIONABBR]DataFrame降維:不需要PERMNO(公司編號)、COMNAM(公司全寫)、TICKER(公司簡寫)這三列數據,刪除列。
sharePricesAAPL.drop(['PERMNO', 'COMNAM', 'TICKER'], axis=1, inplace=True)索引數據類型檢測:確保相應索引的數據類型為float。
sharePricesAAPL.info() <class 'pandas.core.frame.DataFrame'> Int64Index: 126 entries, 163028 to 163153 Data columns (total 6 columns):# Column Non-Null Count Dtype --- ------ -------------- ----- 0 date 126 non-null int64 1 BIDLO 126 non-null float642 ASKHI 126 non-null float643 PRC 126 non-null float644 VOL 126 non-null float645 OPENPRC 126 non-null float64 dtypes: float64(5), int64(1) memory usage: 6.9 KB索引檢查:檢查date索引是否存在重復。
sharePricesAAPL['date'].is_unique True時間序列:將date(日期)轉化為時間序列索引,并按此時間序列以升序排序。
# date列轉化為datetime類 sharePricesAAPL['date'] = sharePricesAAPL['date'].apply(lambda dt: datetime.strptime(str(dt), '%Y%m%d')) # 設date列為索引 sharePricesAAPL.set_index('date', inplace=True) # 按date升序排列 sharePricesAAPL.sort_values(by='date', inplace=True, ascending=True)| 183.42000 | 187.30 | 187.17999 | 17612113.0 | 183.82001 |
| 183.53999 | 187.95 | 183.92000 | 13909764.0 | 187.78999 |
| 184.28000 | 186.41 | 185.39999 | 16592763.0 | 185.25999 |
| ... | ... | ... | ... | ... |
| 150.07001 | 156.77 | 156.14999 | 53117005.0 | 155.84000 |
| 154.55000 | 158.52 | 156.23000 | 42291347.0 | 157.50000 |
| 156.48000 | 159.36 | 157.74001 | 35003466.0 | 158.53000 |
126 rows × 5 columns
缺失值處理:檢查AAPL股票技術指標數據每列缺失比,發現無缺失。若有,則可對BIDLO(最低價)、ASKHI(最高價)、PRC收盤價、VOL(成交量)有缺失的數據行直接刪除。對OPENPRC(開盤價)有缺失的使用拉格朗日插值法進行填充。
其實之后對股票價格.csv分析可知,缺失項的分布都在同一行,故只要使用df.dropna()刪除存在任意數目缺失項的行即可。
sharePricesAAPL.isnull().mean() BIDLO 0.0 ASKHI 0.0 PRC 0.0 VOL 0.0 OPENPRC 0.0 dtype: float64重建索引:重命名索引,方便后期使用,映射為BIDLO-low、ASKHI-high、PRC-close、VOL-vol、OPENPRC-open。改變索引順序為open、high、low、vol、close。
# rename AAPL_newIndex = {'BIDLO': 'low','ASKHI': 'high','PRC': 'close','VOL': 'vol','OPENPRC': 'open'} sharePricesAAPL.rename(columns=AAPL_newIndex, inplace=True) # reindex AAPL_newColOrder = ['open', 'high', 'low', 'vol', 'close'] sharePricesAAPL = sharePricesAAPL.reindex(columns=AAPL_newColOrder)檢測過濾異常值:無異常。
sharePricesAAPL.describe()| 126.000000 | 126.000000 | 126.000000 | 1.260000e+02 | 126.000000 |
| 201.247420 | 203.380885 | 198.893344 | 3.510172e+07 | 201.106033 |
| 21.368524 | 21.499932 | 21.596966 | 1.577876e+07 | 21.663971 |
| ... | ... | ... | ... | ... |
| 207.320000 | 209.375000 | 205.785150 | 3.234006e+07 | 207.760005 |
| 219.155000 | 222.172503 | 216.798175 | 4.188390e+07 | 219.602500 |
| 230.780000 | 233.470000 | 229.780000 | 9.624355e+07 | 232.070010 |
8 rows × 5 columns
數據存儲:存儲處理好的數據為AAPL股票價格.csv,存至補充數據1925102007文件夾。方便后續讀取使用。
sharePricesAAPL.to_csv('補充數據1925102007/AAPL股票價格.csv')3.3.2 論壇.csv
| ComputerBlue | 31-Dec-18 | Let's create a small spec POS portfolio $COTY ... | https://seekingalpha.com/symbol/COTY |
| Darren McCammon | 31-Dec-18 | $RICK "Now that we've reported results, we'll ... | https://seekingalpha.com/symbol/RICK |
| Jonathan Cooper | 31-Dec-18 | Do any $APHA shareholders support the $GGB tak... | https://seekingalpha.com/symbol/APHA |
| ... | ... | ... | ... |
| Power Hedge | 1-Jan-18 | USD Expected to Collapse in 2018 https://goo.g... | https://goo.gl/RG1CDd |
| Norman Tweed | 1-Jan-18 | Happy New Year everyone! I'm adding to $MORL @... | https://seekingalpha.com/symbol/MORL |
| User 40986305 | 1-Jan-18 | Jamie Diamond says Trump is most pro business ... | NaN |
25117 rows × 4 columns
缺失值處理:刪除字段4(各公司頁面的URL)缺失的數據行。
forum = pd.read_csv('論壇.csv') forum.dropna(inplace=True)字符串操作和正則:觀察字段4(URL),seekingalpha.com/symbol/網址后的內容為公司簡稱,使用pandas字符串操作和正則對公司簡稱進行提取,提取失敗則刪除該數據行。將字段4的數據內容替換為公司簡稱。
forum_regExp = re.compile(r'seekingalpha\.com/symbol/([A-Z]+)') def forumAbbr(link):# 成功查找公司簡稱則返回簡稱,否則以缺失值填補res = forum_regExp.search(link)return np.NAN if res is None else res.group(1) forum['字段4_鏈接'] = forum['字段4_鏈接'].apply(forumAbbr)索引過濾:提取所有公司簡稱為AAPL的評論。
降維處理:字段1(作者名稱)無用,可以刪除。
索引重構:重命名索引,字段3(帖子內容)-remark。
時間序列:將字段2轉化為時間序列索引,命名為date,并按此索引升序排列。
# 索引過濾 forum = forum[forum['字段4_鏈接']==CORPORATIONABBR] # 降維處理 forum.drop(['字段1', '字段4_鏈接'], axis=1, inplace=True) # 索引重構 AAPL_newIndex_forum = {'字段2': 'date', '字段3': 'remark'} forum.rename(columns=AAPL_newIndex_forum, inplace=True) # 時間序列 forum['date'] = forum['date'].apply(lambda dt: datetime.strptime(str(dt), '%d-%b-%y'))正則過濾評論網址:觀察評論不難發現,部分評論內有網址,使用正則表達式過濾之,防止對后續情感分析產生影響。
forum_regExp_linkFilter = re.compile(r'(http|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?') forum['remark'] = forum['remark'].apply(lambda x: forum_regExp_linkFilter.sub('', x)) forum| 2018-12-26 | Many Chinese companies are encouraging their e... |
| 2018-12-21 | This Week in Germany 🇩🇪 | Apple Smashed 📱 $AAP... |
| 2018-12-21 | $AAPL gets hit with another partial ban in Ger... |
| ... | ... |
| 2018-01-05 | $AAPL. Claims by GHH is 200 billion repatriati... |
| 2018-01-03 | $AAPL Barclays says battery replacement could ... |
| 2018-01-02 | 2018 will be the year for $AAPL to hit the 1 t... |
330 rows × 2 columns
同時,在進行情感分析時,應增加停用詞AAPL.
數據存儲:存儲為補充數據1925102007/AAPL論壇.csv。
# 數據儲存 forum.to_csv('補充數據1925102007/AAPL論壇.csv', index=False)3.3.3 全標題.xlsx
| Micron Technology: Insanely Cheap Stock Given ... | https://seekingalpha.com/article/4230920-micro... | MU | Dec. 31, 2018, 7:57 PM | Ruerd Heeg | 75?Comments | NaN |
| Molson Coors Seems Attractive At These Valuations | https://seekingalpha.com/article/4230922-molso... | TAP | Dec. 31, 2018, 7:44 PM | Sanjit Deepalam | 16?Comments | NaN |
| Gerdau: The Brazilian Play On U.S. Steel | https://seekingalpha.com/article/4230917-gerda... | GGB | Dec. 31, 2018, 7:10 PM | Shannon Bruce | 1?Comment | NaN |
| ... | ... | ... | ... | ... | ... | ... |
| Big Changes For Centurylink, AT&T And Verizon ... | https://seekingalpha.com/article/4134687-big-c... | CTL, T, VZ | Jan. 1, 2018, 5:38 AM | EconDad | 32?Comments | NaN |
| UPS: If The Founders Were Alive Today | https://seekingalpha.com/article/4134684-ups-f... | UPS | Jan. 1, 2018, 5:11 AM | Roger Gaebel | 15?Comments | NaN |
| U.S. Silica - Buying The Dip Of This Booming C... | https://seekingalpha.com/article/4134664-u-s-s... | SLCA | Jan. 1, 2018, 12:20 AM | The Value Investor | 27?Comments | NaN |
17928 rows × 7 columns
索引過濾:提取所有ABOUT為AAPL的標題數據行。
降維處理:字段1_鏈接_鏈接、ABOUT、AUTHOR、COMMENTS、Unnamed: 6列刪除。
索引重構:重命名索引,標題-title、ABOUT-abbr、TIME-date。
時間序列:將date轉化為時間序列索引,并按此索引升序排列。
數據存儲:存儲為補充數據1925102007/AAPL全標題.csv。
allTitles = pd.read_excel('全標題.xlsx') # 索引過濾 allTitles = allTitles[allTitles['ABOUT']==CORPORATIONABBR] # 降維 allTitles.drop(['字段1_鏈接_鏈接','ABOUT','AUTHOR','COMMENTS','Unnamed: 6'], axis=1, inplace=True) # 索引重構 AAPL_newIndex_allTitles = {'標題': 'title', 'TIME': 'date'} allTitles.rename(columns=AAPL_newIndex_allTitles, inplace=True) # 時間序列處理 # 因時間日期格式非統一,故選用dateutil包對parser.parse方法識別多變時間格式 allTitles['date'] = allTitles['date'].apply(lambda dt: dt_parse(dt)) # 設date列為索引 allTitles.set_index('date', inplace=True) # 按date升序排列 allTitles.sort_values(by='date', inplace=True, ascending=True) # 數據儲存 allTitles.to_csv('補充數據1925102007/AAPL全標題.csv') allTitles| Apple Ia Above A 'Golden Cross' And Has A Posi... |
| Apple Cash: What Would Warren Buffett Say? |
| Apple's iPhone Battery Replacement Could Consu... |
| ... |
| Will Apple Beat Its Guidance? |
| How Much Stock Could Apple Have Repurchased In... |
| Will Apple Get Its Mojo Back? |
204 rows × 1 columns
3.3.4 摘要.xlsx
| HealthEquity: Strong Growth May Be Slowing Hea... | Apr. 1, 2019 10:46 PM ET | | About: HealthEquity, Inc. (HQY) | SummaryHealthEquity’s revenue and earnings hav... |
| Valero May Rally Up To 40% Within The Next 12 ... | Apr. 1, 2019 10:38 PM ET | | About: Valero Energy Corporation (VLO) | SummaryValero is ideally positioned to benefit... |
| Apple Makes A China Move | Apr. 1, 2019 7:21 PM ET | | About: Apple Inc. (AAPL) | SummaryCompany cuts prices on many key product... |
| ... | ... | ... | ... |
| Rubicon Technology: A Promising Net-Net Cash-B... | Jul. 24, 2018 2:16 PM ET | | About: Rubicon Technology, Inc. (RBCN) | SummaryRubicon is trading well below likely li... |
| Stamps.com: A Cash Machine | Jul. 24, 2018 1:57 PM ET | | About: Stamps.com Inc. (STMP) | SummaryThe Momentum Growth Quotient for the co... |
| Can Heineken Turn The 'Mallya Drama' In Its Ow... | Jul. 24, 2018 1:24 PM ET | | About: Heineken N.V. (HEINY), Includes: BUD,... | SummaryMallya, United Breweries' chairman, can... |
10131 rows × 4 columns
經檢查,摘要.xlsx無缺失值,我們只需要標題和字段1(摘要的全文字內容),其余數據列刪去。將索引映射為:標題-title、字段1-abstract.
abstracts = pd.read_excel('摘要.xlsx') abstracts.drop(['字段2', '字段5'], axis=1, inplace=True) newIndex_abstracts = {'標題': 'title', '字段1': 'abstract'} abstracts.rename(columns=newIndex_abstracts, inplace=True)求交集:和AAPL全標題.csv中title相對應的數據行是針對AAPL股票公司文章的摘要,只需要對AAPL文章的摘要即可。
abstracts = abstracts.merge(allTitles, on=['title'], how='inner')保存:存儲為補充數據1925102007/AAPL摘要.csv。
abstracts.to_csv('補充數據1925102007/AAPL摘要.csv', index=False) abstracts| Will Apple Get Its Mojo Back? | SummaryApple has been resting on a reputation ... |
| How Much Stock Could Apple Have Repurchased In... | SummaryApple's stock plummeted from $227.26 to... |
| Will Apple Beat Its Guidance? | SummaryApple has sold fewer iPhones, which gen... |
| ... | ... |
| Apple: Still The Ultimate Value Growth Stock T... | SummaryApple reported superb earnings on Tuesd... |
| Apple In 2023 | SummaryWhere can the iPhone go from here?The A... |
| Apple's Real Value Today | SummaryApple has reached new highs this week.W... |
86 rows × 2 columns
3.3.5 回帖
pd.read_excel('回帖/SA_Comment_Page131-153.xlsx')| you should all switch to instagram | NaN |
| Long Facebook and Instagram. They will recover... | NaN |
| Personally, I think people will be buying FB a... | NaN |
| ... | ... |
| Thank you for the article.If you really think ... | Qiwi: The Current Sell-Off Was Too Emotional |
| Isn't WRK much better investment than PKG? Thanks | NaN |
| GuruFocus is also showing a Priotroski score o... | Packaging Corporation Of America: Target Retur... |
19971 rows × 2 columns
pd.read_csv('回帖/SA_Comment_Page181-255(1).csv')| I bought at $95 and holding strong. Glad I did... | NaN |
| The price rally you are referring to is not be... | Michael Kors: Potential For Further Upside Ahead |
| only a concern if you own it.... | NaN |
| ... | ... |
| What can Enron Musk do legally to boost balan... | NaN |
| The last two weeks feels like a short squeeze.... | NaN |
| " Tesla is no longer a growth or value proposi... | NaN |
20000 rows × 2 columns
索引重命名:字段1(回帖內容)-content、標題-title.(注意.csv和.xlsx不同)
缺失值處理:對于回帖中標題1(各文章標題)的定義空標題的,用最靠近的有內容的下方標題,故采取用下一個非缺失值填充前缺失值的方法df.fillna(method='bfill')。
數據文件讀取:使用os.listdir()返回指定文件夾下包含的文件名列表,以.xlsx或.csv結尾的文件均為數據文件,讀入后進行上述缺失值處理和索引重命名。
回帖過濾:遍歷所有數據文件,找出所有title在AAPL全標題.csv中的回帖行數據,檢查是否有缺失,存至補充數據1925102007/AAPL回帖.csv
# 數據文件讀取 repliesFiles = listdir('回帖') allAALPReplies = [] newIndex_replies_csv = {'字段1': 'content', '標題': 'title'} newIndex_replies_xlsx = {'字段': 'content', '標題1': 'title'} # 遍歷回帖目錄下所有回帖數據找出和AAPL相關的回帖 for file in repliesFiles:path = '回帖/'+fileif file.endswith('.csv'):replies = pd.read_csv(path)newIndex_replies = newIndex_replies_csvelif file.endswith('.xlsx'):replies = pd.read_excel(path)newIndex_replies = newIndex_replies_xlsxelse:print('Wrong file format,', file)break# 索引重命名replies.rename(columns=newIndex_replies, inplace=True)# 缺失值填充replies.fillna(method='bfill', inplace=True)# 回帖過濾allAALPReplies.extend(replies.merge(allTitles, on=['title'], how='inner').values) # 所有和AAPL文章標題所對應的回帖 allAALPReplies = pd.DataFrame(allAALPReplies, columns=['content', 'title']) # 保存 allAALPReplies.to_csv('補充數據1925102007/AAPL回帖.csv', index=False) # 展示 allAALPReplies| Understood. But let me ask you. 64GB of pics i... | iPhone XR And XS May Be Apple's Most Profitabl... |
| Just upgraded from 6 to XS, 256G. Love it. I'l... | iPhone XR And XS May Be Apple's Most Profitabl... |
| Yup, AAPL will grow profits 20% per year despi... | iPhone XR And XS May Be Apple's Most Profitabl... |
| ... | ... |
| With all due respect, never have paid for and ... | Gain Exposure To Apple Through Berkshire Hathaway |
| This one's easy - own both! | Gain Exposure To Apple Through Berkshire Hathaway |
| No Thanks! I like my divys,and splits too much... | Gain Exposure To Apple Through Berkshire Hathaway |
4506 rows × 2 columns
3.4 情感分析
使用第三方NLP庫:NLTK (Natural Language Toolkit)
NLTK is a leading platform for building Python programs to work with human language data. It provides easy-to-use interfaces such as WordNet, along with a suite of text processing libraries for classification, tokenization, stemming, tagging, parsing, and semantic reasoning, wrappers for industrial-strength NLP libraries.
安裝完nltk庫以后,需要使用nltk.download()命令下載相應語料庫。因為速度太慢,我選擇直接裝nltk_data數據包,核心數據包放在補充文件夾內。
為提高情感分析效率和精度,停用詞還需增加['!', ',' ,'.' ,'?' ,'-s' ,'-ly' ,'</s> ', 's', 'AAPL', 'apple', '$', '%']. 使用stopwords.add()添加停用詞。
[Source3] http://www.nltk.org
金融情感詞庫:LM (LoughranMcDonald) sentiment word lists 2018
[Loughran-McDonald Sentiment Word Lists](https://sraf.nd.edu/textual-analysis/resources/#LM Sentiment Word Lists) is an Excel file containing each of the LM sentiment words by category (Negative, Positive, Uncertainty, Litigious, Strong Modal, Weak Modal, Constraining).
詞庫路徑:/補充數據1925102007/LoughranMcDonald_SentimentWordLists_2018.xlsx
[Source4] https://sraf.nd.edu/textual-analysis/resources
3.4.1 情感分析思路
- 分詞處理:使用NLTK對文本(這里指評論數據)進行分詞處理(tokenize)
- 停用詞處理:去除停用詞(stopwords)
- 結構化:利用LM金融情感詞庫中的Positive和Negative表單詞庫,計算pos和neg值作為非結構化文本數據的結構化特征。(即以評論中posWords和negWords的占比作為文本數據的特征)
- 數據聚合:對上述數據進行聚合操作,并按工作日(股票的交易時間是Business Day)為單位進行重采樣
pos=NumofPosWrodsTotalWordspos = \frac{Num of PosWrods}{Total Words} pos=TotalWordsNumofPosWrods?
neg=NumofNegWrodsTotalWordsneg = \frac{Num of NegWrods}{Total Words} neg=TotalWordsNumofNegWrods?
3.4.2 詞庫導入和添加停用詞
# 詞庫導入 wordListsPath = '補充數據1925102007/LoughranMcDonald_SentimentWordLists_2018.xlsx' posWords = pd.read_excel(wordListsPath, header=None, sheet_name='Positive').iloc[:,0].values negWords = pd.read_excel(wordListsPath, header=None, sheet_name='Negative').iloc[:,0].values# 添加停用詞 extraStopwords = ['!', ',' ,'.' ,'?' ,'-s' ,'-ly' ,'</s> ', 's', 'AAPL', 'apple', '$', '%'] stopWs = stopwords.words('english') + extraStopwords3.4.3 函數定義
def structComment(sentence, posW, negW, stopW):"""結構化句子:param sentence: 待結構化的評論:param posW: 正詞性:param negW: 負詞性:param stopW: 停用詞:return: 去除停用詞后的評論中posWords和negWords的占比(pos, neg)"""# 分詞tokenizer = nltk.word_tokenize(sentence)# 停用詞過濾tokenizer = [w.upper() for w in tokenizer if w.lower() not in stopW]# 正詞提取posWs = [w for w in tokenizer if w in posW]# 負詞提取negWs = [w for w in tokenizer if w in negW]# tokenizer長度len_token = len(tokenizer)# 句子長度為0,即分母為0時if len_token<=0:return 0, 0else:return len(posWs)/len_token, len(negWs)/len_token def NLProcessing(fileName, colName):"""自然語言處理方法:將傳入的fileName(.csv)對應的數據中的colName列文本數據結構化,并保存:param fileName: 文件名,在文件夾 補充數據1925102007/ 下查找對應文件:param colName: 需要結構化的文本數據列:return: 新增pos和neg列的DataFrame"""pathNLP = '補充數據1925102007/'+fileName+'.csv'data = pd.read_csv(pathNLP)# pos和neg結構化數據列構造posAndneg = [ structComment(st, posWords, negWords, stopWs) for st in data[colName].values]# 構造posAndneg的DataFrameposAndneg = pd.DataFrame(posAndneg, columns=['pos', 'neg'])# 軸向連接data = pd.concat([data, posAndneg], axis=1)# 刪除文本數據列data.drop([colName], axis=1, inplace=True)# 保存結構化的數據data.to_csv(pathNLP)return data3.4.4 情感分析處理
# AAPL論壇.csv forum = NLProcessing('AAPL論壇', 'remark') # AAPL摘要.csv abstracts = NLProcessing('AAPL摘要', 'abstract') # AAPL回帖.csv allAALPReplies = NLProcessing('AAPL回帖', 'content')3.4.5 情感特征數據聚合
上述操作得到帶有title列的結構化數據(AAPL回帖.csv和AAPL摘要.csv)后,先將回帖和摘要用concat函數沿縱軸連接,再以title為索引,與AAPL全標題.csv(allTitles)進行外聯合并(Outer Merge),刪除無用的title列。forum結構化數據和上一步所得數據進行concat軸相連接(沿縱軸)。最后,以時間天為單位進行重采樣,得出每日的pos和neg特征的平均值。
# 軸相連接abstracts和allAALPReplies allEssaysComment = pd.concat([abstracts,allAALPReplies], ignore_index=True) # 聯表 allEssaysComment = allTitles.merge(allEssaysComment, how='outer', on='title') # 刪除缺失行 allEssaysComment.dropna(inplace=True) # 刪除title列 allEssaysComment.drop('title', axis=1, inplace=True) # 和forum情感數據進行軸向連接 allEssaysComment = pd.concat([allEssaysComment,forum], ignore_index=True) # 刪除pos和neg均為0的無用數據行 allEssaysComment = allEssaysComment[(allEssaysComment['pos']+allEssaysComment['neg'])>0]# 設date為時間序列索引 allEssaysComment['date'] = pd.to_datetime(allEssaysComment['date']) allEssaysComment.set_index('date', inplace=True) # 按"工作日"重采樣,求pos和neg的均值,不存在的天以0填充 allEssaysComment = allEssaysComment.resample('B').mean() allEssaysComment.fillna(0, inplace=True) # 儲存 allEssaysComment.to_csv('補充數據1925102007/allPosAndNeg.csv') # 展示 allEssaysComment| 0.041667 | 0.043478 |
| 0.000000 | 0.000000 |
| 0.000000 | 0.090909 |
| ... | ... |
| 0.000000 | 0.000000 |
| 0.000000 | 0.000000 |
| 0.090909 | 0.090909 |
254 rows × 2 columns
3.5 * 融入情感數據的股票指標相關性分析
方法:希望借助seaborn的pairplot函數繪制AAPL股票價格.csv(sharePricesAAPL)的各項指標數據兩兩關聯的散點圖(對角線為變量的直方圖),從而探究不同指標間的關系。
目的:分析股票各指標間的關系。以及是否找出線性相關程度高的指標,刪除之,以減少LSTM的訓練時間成本。
pairplot函數文檔:http://seaborn.pydata.org/generated/seaborn.pairplot.html
3.5.1 數據聯合
將2.2所得時間序列情感分析數據(allPosAndNeg.csv)和AAPL股票價格.csv(sharePricesAAPL)以date為索引合并。
聯合時可以發現,評論數據的時間跨度足以覆蓋AAPL股票價格數據,所以不用擔心缺失值的問題。 [Jump to relative contents]
# 文件讀取 sharePricesAAPL = pd.read_csv('補充數據1925102007/AAPL股票價格.csv') allPosAndNeg = pd.read_csv('補充數據1925102007/allPosAndNeg.csv') # 合并 sharePricesAAPLwithEmotion = sharePricesAAPL.merge(allPosAndNeg, how='inner', on='date') # 序列化時間索引date sharePricesAAPLwithEmotion['date'] = pd.DatetimeIndex(sharePricesAAPLwithEmotion['date']) sharePricesAAPLwithEmotion.set_index('date', inplace=True) # reindex AAPL_newColOrder_emotionPrices = ['open', 'high', 'low', 'vol', 'pos', 'neg', 'close'] sharePricesAAPLwithEmotion = sharePricesAAPLwithEmotion.reindex(columns=AAPL_newColOrder_emotionPrices) # 保存 sharePricesAAPLwithEmotion.to_csv('補充數據1925102007/AAPL股票價格融合情感.csv')3.5.2 pairplot繪圖
留下必要的OHLC技術指標,對剩余的vol、pos、neg進行相關性分析繪圖
實驗時,我也繪制了OHLC技術指標的軸線網格圖,可以發現,其兩兩間具有較高的線性相關性。
# Parameters: # data: pandas.DataFrame [Tidy (long-form) dataframe where each column is a variable and each row is an observation.] # diag_kind: {‘auto’, ‘hist’, ‘kde’, None} [Kind of plot for the diagonal subplots.] # kind: {‘scatter’, ‘kde’, ‘hist’, ‘reg’} [Kind of plot to make.] fig1 = pairplot(sharePricesAAPLwithEmotion[['vol', 'pos', 'neg']], diag_kind='hist', kind='reg') # save the fig1 to 補充數據1925102007/ fig1.savefig('補充數據1925102007/fig1_a_Grid_of_Axes.png')3.5.3 股票指標相關性分析
觀察所得Fig1: a Grid of Axes不難發現,指標vol、pos、neg之間線性相關性較弱,所以均保留,作為LSTM預測指標。
3.6 LSTM預測融合情感特征的股票數據
依賴的庫:Keras、Sklearn、Tensorflow [4]
預測目標:close(收盤價)
引用函數:series_to_supervised(data, n_in=1, n_out=1, dropnan=True)
來源:Time Series Forecasting With Python
用途:Frame a time series as a supervised learning dataset. 將輸入的單變量或多變量時間序列轉化為有監督學習數據集。
參數(Arguments):
data: Sequence of observations as a list or NumPy array.
n_in: Number of lag observations as input (X).
n_out: Number of observations as output (y).
dropnan: Boolean whether or not to drop rows with NaN values.
# 因為LSTM已經具有記憶功能了,所以我的n_in和n_out參數直接使用默認的1即可(也就是構造[t-1]現態列和[t]次態列)。
返回值(Returns):
Pandas DataFrame of series framed for supervised learning.
3.6.1 時間序列轉有監督函數定義
def series_to_supervised(data, n_in=1):# 默認參數n_out=1dropnan=True# 對該函數進行微調,注意data為以close列(需要預測的列)結尾的DataFrame時間序列股票數據n_vars = 1 if type(data) is list else data.shape[1]df = pd.DataFrame(data)cols, names = list(), list()# input sequence (t-n, ... t-1)for i in range(n_in, 0, -1):cols.append(df.shift(i))names += [('var%d(t-%d)' % (j+1, i)) for j in range(n_vars)]# forecast sequence (t, t+1, ... t+n)for i in range(0, n_out):cols.append(df.shift(-i))if i == 0:names += [('var%d(t)' % (j+1)) for j in range(n_vars)]else:names += [('var%d(t+%d)' % (j+1, i)) for j in range(n_vars)]# put it all togetheragg = pd.concat(cols, axis=1)agg.columns = names# 刪除無關的次態[t]列,只留下需要預測的close[t]列和上一時刻狀態特征[t-1]列agg.drop(agg.columns[[x for x in range(data.shape[1], 2*data.shape[1]-1)]], axis=1, inplace=True)# drop rows with NaN valuesif dropnan:agg.dropna(inplace=True)return agg3.6.2 融合情感的股票數據歸一化
# 讀取數據 sharePricesAAPLwithEmotion = pd.read_csv('補充數據1925102007/AAPL股票價格融合情感.csv', parse_dates=['date'], index_col='date').values # 生成歸一化容器 # feature_range參數沿用默認(0,1) scaler = MinMaxScaler() # 訓練模型 scaler = scaler.fit(sharePricesAAPLwithEmotion) # 歸一化 sharePricesAAPLwithEmotion = scaler.fit_transform(sharePricesAAPLwithEmotion) # 部分結果展示 sharePricesAAPLwithEmotion[:5,:] array([[0.4316836 , 0.43640137, 0.44272148, 0.06118638, 0. ,0. , 0.47336914],[0.47972885, 0.44433594, 0.44416384, 0.01698249, 0. ,0. , 0.4351243 ],[0.44911044, 0.42553711, 0.45305926, 0.04901593, 0. ,0. , 0.45248692],[0.4510469 , 0.45024426, 0.46411828, 0.05954544, 0. ,0. , 0.4826372 ],[0.50042364, 0.47766101, 0.51340305, 0.08659896, 0. ,0. , 0.51325663]])3.6.3 時間序列構建有監督數據集
# 使用series_to_supervised函數構建有監督數據集 sharePricesAAPLwithEmotion = series_to_supervised(sharePricesAAPLwithEmotion) sharePricesAAPLwithEmotion| 0.431684 | 0.436401 | 0.442721 | 0.061186 | 0.0 | 0.0 | 0.473369 | 0.435124 |
| 0.479729 | 0.444336 | 0.444164 | 0.016982 | 0.0 | 0.0 | 0.435124 | 0.452487 |
| 0.449110 | 0.425537 | 0.453059 | 0.049016 | 0.0 | 0.0 | 0.452487 | 0.482637 |
| ... | ... | ... | ... | ... | ... | ... | ... |
| 0.148251 | 0.128906 | 0.104700 | 0.624252 | 0.0 | 0.0 | 0.117316 | 0.045753 |
| 0.105410 | 0.080688 | 0.036543 | 0.994059 | 0.0 | 0.0 | 0.045753 | 0.000000 |
| 0.000000 | 0.000000 | 0.000000 | 0.295643 | 0.0 | 0.0 | 0.000000 | 0.121305 |
122 rows × 8 columns
3.6.4 訓練集驗證集劃分
# 必須規定ndarray的dtype為float32(默認float64),否則后續輸入LSTM模型報錯 sharePricesAAPLwithEmotion = sharePricesAAPLwithEmotion.values.astype(np.float32) # 訓練集:驗證集=7:3 X_train, X_test, y_train, y_test = train_test_split(sharePricesAAPLwithEmotion[:,:-1], sharePricesAAPLwithEmotion[:,-1], test_size=0.3, shuffle=False)3.6.5 基于Keras的LSTM模型搭建
參考文檔:
Keras core: Dense and Dropout
Keras Activation relu
Keras Losses mean_squared_error
Keras Optimizer adam
Keras LSTM Layers
Keras Sequential Model
3.6.5 (一)、重塑LSTM的輸入X
LSTM的輸入格式為**shape = [samples,timesteps,features]**:
samples:樣本數量
timesteps:時間步長
features (input_dim):每一個時間步上的維度
重塑X_train和X_test:
# reshape input to be 3D [samples, timesteps, features] X_train = X_train.reshape((X_train.shape[0], 1, X_train.shape[1])) X_test = X_test.reshape((X_test.shape[0], 1, X_test.shape[1]))3.6.5 (二)、搭建LSTM模型并繪制損失圖
- 建立Sequential模型
- 添加LSTM層(64個隱藏層神經元,1個輸出層神經元,指定多層LSTM模型第一層的input_shape參數)回歸模型
- 設定Dropout在每次訓練時的丟棄比(rate)為0.4
- 設定Dense全連接層的輸出空間維度(units)為1,激活函數(activation)為relu(整流線性單元)
- 設定Sequential的損失函數(loss)為MSE(Mean-Square Error)均方誤差,優化器(optimizer)為adam
- 模型訓練設置epochs=50; batch_size=30
- 損失圖繪制
-
損失圖分析:
由Fig2含情感的股票價格LSTM損失圖可以看出,MSE隨迭代次數增加而減小,在大約30次迭代后,其趨于穩定(收斂)。
3.6.6 預測結果并反歸一化
# 因為只要對結果列進行反歸一化操作, # 故不用inverse_transform函數, # 這里自定義對某列的反歸一化函數 inverse_transform_col def inverse_transform_col(_scaler, y, n_col):"""對某個列進行反歸一化處理的函數:param _scaler: sklearn歸一化模型:param y: 需要反歸一化的數據列:param n_col: y在歸一化時所屬的列編號:return: y的反歸一化結果"""y = y.copy()y -= _scaler.min_[n_col]y /= _scaler.scale_[n_col]return y # 模型預測結果繪圖函數 def predictGraph(yTrain, yPredict, yTest, timelabels, title, num):"""預測結果圖像繪制函數:param yTrain: 訓練集結果:param yPredict: 驗證集的預測結果:param yTest: 驗證集的真實結果:param timelabels: x軸刻度標簽:param title: 圖表標題:param num: 圖標編號:return: 無"""len_yTrain = yTrain.shape[0]len_y = len_yTrain+yPredict.shape[0]# 真實曲線繪制plt.plot(np.concatenate([yTrain,yTest]), color='r', label='sample')# 預測曲線繪制plt.plot([x for x in range(len_yTrain,len_y)],yPredict, color='g', label='predict')# 標題和軸標簽plt.title('Fig'+num+'. '+title)plt.xlabel('date')plt.ylabel('close')plt.legend()# 刻度和刻度標簽xticks = [0,len_yTrain,len_y-1]xtick_labels = [timelabels[x] for x in xticks]plt.xticks(ticks=xticks, labels=xtick_labels, rotation=30)# 保存于 補充數據1925102007/savingPath = '補充數據1925102007/fig'+num+'_'+title.replace(' ', '_')+'.png'plt.savefig(savingPath, dpi=400, bbox_inches='tight')# 展示plt.show() # 由X_test前日股票指標預測當天股票close值 # 注:predict生成的array需降維成 shape=(n_samples, ) y_predict = model.predict(X_test)[:,0]# 反歸一化 # 重新讀取 AAPL股票價格融合情感.csv sharePricesAAPLwithEmotion = pd.read_csv('補充數據1925102007/AAPL股票價格融合情感.csv') col_n = sharePricesAAPLwithEmotion.shape[1]-2 # 預測結果反歸一化 inv_yPredict = inverse_transform_col(scaler, y_predict, col_n) # 真實結果反歸一化 inv_yTest = inverse_transform_col(scaler, y_test, col_n) # 訓練集結果反歸一化(以繪制完整圖像) inv_yTrain = inverse_transform_col(scaler, y_train, col_n) # 繪圖 predictGraph(inv_yTrain, inv_yPredict, inv_yTest, timelabels=sharePricesAAPLwithEmotion['date'].values, title='Prediction Graph of Stock Prices with Emotions', num='3')3.6.7 模型評估
誤差評價方法:MSE
# sklearn.metrics.mean_squared_error(y_true, y_pred) mse = mean_squared_error(inv_yTest, inv_yPredict) print('帶有情感特征的股票數據預測結果的均方誤差(MSE)為 ', mse) 帶有情感特征的股票數據預測結果的均方誤差(MSE)為 160.42007分析:
觀察Fig3可知,用含有情感特征的股票數據訓練的LSTM模型預測結果(綠色曲線)和真實結果(紅色曲線的后段)總體變化趨勢一致,即真實值下降或上升時,預測值跟著下降或上升。在模型預測的開始階段,擬合效果較好,但隨著時間推移,預測值和真實值的結果差距愈發增大。
3.7 對比實驗:預測純技術指標的股票數據
作為對比,導入補充數據1925102007/AAPL股票價格.csv,具體操作和上述一致,對不含情感特征的純技術指標股票數據進行預測分析。
(操作基本一致,故不作詳細注釋)
3.7.1 對比實驗流程(通用函數構造)
def formatData(sharePricesData):"""模式化樣本數據的函數:param sharePricesData: 樣本數據的DataFrame:return: X_train, X_test, y_train, y_test, scaler"""# 歸一化_scaler = MinMaxScaler()_scaler = _scaler.fit(sharePricesData)sharePricesData = _scaler.fit_transform(sharePricesData)# 構建有監督數據集sharePricesData = series_to_supervised(sharePricesData)# dtype為float32sharePricesData = sharePricesData.values.astype(np.float32)# 訓練集和驗證集的劃分_X_train, _X_test, _y_train, _y_test = train_test_split(sharePricesData[:,:-1], sharePricesData[:,-1], test_size=0.3, shuffle=False)# reshape input_X_train = _X_train.reshape((_X_train.shape[0], 1, _X_train.shape[1]))_X_test = _X_test.reshape((_X_test.shape[0], 1, _X_test.shape[1]))return _X_train, _X_test, _y_train, _y_test, _scaler def invTransformMulti(_scaler, _y_predict, _y_test, _y_train, _col_n):# 批量反歸一化_inv_yPredict = inverse_transform_col(_scaler, _y_predict, _col_n)_inv_yTest = inverse_transform_col(_scaler, _y_test, _col_n)_inv_yTrain = inverse_transform_col(_scaler, _y_train, _col_n)return _inv_yPredict, _inv_yTest, _inv_yTrain # 讀取數據 sharePricesAAPL = pd.read_csv('補充數據1925102007/AAPL股票價格.csv', parse_dates=['date'], index_col='date').values # 標準化數據輸入 X_train, X_test, y_train, y_test, scaler = formatData(sharePricesAAPL) # 建模 history, model = LSTMModelGenerate(X_train, X_test, y_train, y_test) # 損失函數繪圖 drawLossGraph(history, title='LSTM Loss Graph for Stock Prices without Emotions', num='4') # 預測 y_predict = model.predict(X_test)[:,0] # 反歸一化 sharePricesAAPL = pd.read_csv('補充數據1925102007/AAPL股票價格.csv') col_n = sharePricesAAPL.shape[1]-2 inv_yPredict, inv_yTest, inv_yTrain = invTransformMulti(scaler, y_predict, y_test, y_train, col_n) # 繪圖 predictGraph(inv_yTrain, inv_yPredict, inv_yTest, timelabels=sharePricesAAPL['date'].values, title='Prediction Graph of Stock Prices without Emotions', num='5') # 均方誤差 mse = mean_squared_error(inv_yTest, inv_yPredict) print('無情感特征的純技術指標股票數據預測結果的均方誤差(MSE)為 ', mse) 無情感特征的純技術指標股票數據預測結果的均方誤差(MSE)為 142.502273.7.2 對比實驗結果分析
對比Fig3和Fig5(含情感和不含情感)
-
均方誤差:通過去除情感信息,用LSTM模型得出的純技術指標的股票close預測結果單就誤差來看要優于含情感特征的股票數據預測結果,純技術指標預測的精度更高,總體上更接近于真值。
MSE (含情感特征) = 160.42007
MSE (純技術指標) = 142.50227
-
曲線特征:顯然,含有情感數據信息的預測結果曲線較無情感的預測曲線更靈敏。Fig3(含情感特征)的預測曲線隨真值曲線的升降而漲跌,真值曲線的變化(突變)趨勢較為完整地體現在預測曲線中,而Fig5(純技術指標)的預測曲線隨真值曲線的波動并不明顯。
Fig3. Prediction Graph of Stock Prices with Emotions
Fig5. Prediction Graph of Stock Prices without Emotions
3.7.3 對比實驗結論
在現有數據下,從總體上來看,純技術指標的股票數據預測精度更高,但從局部來看,融入了情感特征的股票數據則更加靈敏。實驗結果基本和預期一致。
結果表明,股票的價格漲跌并非無規律的隨機游走,而是和股民的情感息息相關。在對股票數據的預測中,融入互聯網論壇上股民大眾的情感數據信息,能夠更好地判斷出未來一段時間內股票的漲跌情況,從而幫助判斷股票的最佳購入點和賣出點、分析股票投資風險。情感數據信息有助于在量化投資中輔助股民和數據分析師做出最優決策。
3.8 補充對比實驗:補充AAPL股票技術指標樣本量進行預測
在 數據聯合 步驟時,發現所給補充數據1925102007/AAPL股票價格.csv數據并不能覆蓋所有的評論數據(allPosAndNeg.csv)。
此外,該數據樣本量較少,按訓練集和驗證集7:3比例劃分后,導致訓練集樣本數只有88條。
因此決定使用英為財情股票行情網站所提供的2018年全年AAPL股票工作日純技術指標數據,使用上述方法對收盤價(close)進行預測,和2.5 對比實驗進行對比。
事實上,
AAPL股票價格.csv覆蓋時間為2018-07-02至2018-12-31,
allPosAndNeg.csv覆蓋時間為2018-01-05至2018-12-31.
3.8.1 數據獲取
從英為財情AAPL個股頁面下載近五年AAPL純技術指標股票數據,儲存于補充數據1925102007\AAPLHistoricalData_5years.csv.
3.8.2 數據處理
# 讀取數據 allYearAAPL = pd.read_csv('補充數據1925102007/AAPLHistoricalData_5years.csv', parse_dates=['Date'], index_col='Date') # 時間序列索引切片 allYearAAPL = allYearAAPL['2018-12-31':'2018-01-01'] # 排序 allYearAAPL.sort_index(inplace=True) # 展示 allYearAAPL| $43.065 | 101602160 | $42.54 | $43.075 | $42.315 |
| $43.0575 | 117844160 | $43.1325 | $43.6375 | $42.99 |
| $43.2575 | 89370600 | $43.135 | $43.3675 | $43.02 |
| ... | ... | ... | ... | ... |
| $39.0375 | 206435400 | $38.96 | $39.1925 | $37.5175 |
| $39.0575 | 166962400 | $39.375 | $39.63 | $38.6375 |
| $39.435 | 137997560 | $39.6325 | $39.84 | $39.12 |
251 rows × 5 columns
# pandas字符串切割、Series類型修改(去除$) allYearAAPL[['Close/Last', 'Open', 'High', 'Low']] = allYearAAPL[['Close/Last', 'Open', 'High', 'Low']].apply(lambda x: (x.str[1:]).astype(np.float32)) # reindex allAAPL_newColOrder = ['Open', 'High', 'Low', 'Volume', 'Close/Last'] allYearAAPL = allYearAAPL.reindex(columns=allAAPL_newColOrder) # 保存為AAPL2018allYearData.csv allYearAAPL.to_csv('補充數據1925102007/AAPL2018allYearData.csv') # 展示 allYearAAPL| 42.540001 | 43.075001 | 42.314999 | 101602160 | 43.064999 |
| 43.132500 | 43.637501 | 42.990002 | 117844160 | 43.057499 |
| 43.134998 | 43.367500 | 43.020000 | 89370600 | 43.257500 |
| ... | ... | ... | ... | ... |
| 38.959999 | 39.192501 | 37.517502 | 206435400 | 39.037498 |
| 39.375000 | 39.630001 | 38.637501 | 166962400 | 39.057499 |
| 39.632500 | 39.840000 | 39.119999 | 137997560 | 39.435001 |
251 rows × 5 columns
3.8.3 預測分析
# 標準化數據輸入 X_train, X_test, y_train, y_test, scaler = formatData(allYearAAPL) # 建模 history, model = LSTMModelGenerate(X_train, X_test, y_train, y_test) # 損失函數繪圖 drawLossGraph(history, title='LSTM Loss Graph for 2018 All Year AAPL Stock Prices', num='6') # 預測 y_predict = model.predict(X_test)[:,0] # 反歸一化 allYearAAPL = pd.read_csv('補充數據1925102007/AAPL2018allYearData.csv') col_n = allYearAAPL.shape[1]-2 inv_yPredict, inv_yTest, inv_yTrain = invTransformMulti(scaler, y_predict, y_test, y_train, col_n) # 繪圖 predictGraph(inv_yTrain, inv_yPredict, inv_yTest, timelabels=allYearAAPL['Date'].values, title='Prediction Graph of 2018 All Year AAPL Stock Prices', num='7') # 均方誤差 mse = mean_squared_error(inv_yTest, inv_yPredict) print('2018全年純技術指標AAPL股票數據預測結果的均方誤差(MSE)為 ', mse)3.8.4 結果分析
由Fig7. Prediction Graph of 2018 All Year AAPL Stock Prices、2018全年純技術指標AAPL股票數據預測結果的均方誤差和2.5 不含情感特征的AAPL股票數據預測的對比實驗比較得知,在增加股票的時間序列數據后,即由原本2018-07-02~2018-12-31擴充至2018-01-01~2018-12-31,純技術指標預測的精度大幅提升,LSTM模型的擬合效果極佳。
由此推斷,Fig3.和Fig5.(即未增添數據前的AAPL含情感特征預測圖和純技術指標預測圖)的預測結果精度低,且隨時間推移,預測結果嚴重偏離真值的原因在于樣本數目不足,導致LSTM模型訓練不到位。接下來,將添加補充數據后的2018全年AAPL股票數據融合情感特征,進行含情感特征的股票數據預測,以驗證這一推斷。
3.9 2018全年含情感特征的股票數據預測實驗
3.9.1 情感特征數據聚合
# 文件讀取 allYearAAPL_withEmos = pd.read_csv('補充數據1925102007/AAPL2018allYearData.csv') allPosAndNeg = pd.read_csv('補充數據1925102007/allPosAndNeg.csv') # 合并 allYearAAPL_withEmos = allYearAAPL_withEmos.merge(allPosAndNeg, how='inner', left_on='Date', right_on='date').drop('date', axis=1) # 序列化時間索引date allYearAAPL_withEmos['Date'] = pd.DatetimeIndex(allYearAAPL_withEmos['Date']) allYearAAPL_withEmos.set_index('Date', inplace=True) # reindex allYearAAPLwithEmos_newColOrder = ['Open','High','Low','Volume','pos','neg','Close/Last'] allYearAAPL_withEmos = allYearAAPL_withEmos.reindex(columns=allYearAAPLwithEmos_newColOrder) # 保存 allYearAAPL_withEmos.to_csv('補充數據1925102007/AAPL2018allYearData_withEmos.csv') # 展示 allYearAAPL_withEmos| 43.3600 | 43.8425 | 43.2625 | 94359720 | 0.041667 | 0.043478 | 43.7500 |
| 43.5875 | 43.9025 | 43.4825 | 82095480 | 0.000000 | 0.000000 | 43.5875 |
| 43.6375 | 43.7650 | 43.3525 | 86128800 | 0.000000 | 0.090909 | 43.5825 |
| ... | ... | ... | ... | ... | ... | ... |
| 39.2150 | 39.5400 | 37.4075 | 381991600 | 0.000000 | 0.000000 | 37.6825 |
| 37.0375 | 37.8875 | 36.6475 | 148676920 | 0.000000 | 0.000000 | 36.7075 |
| 37.0750 | 39.3075 | 36.6800 | 232535400 | 0.090909 | 0.090909 | 39.2925 |
245 rows × 7 columns
3.9.2 預測分析
# 標準化數據輸入 X_train, X_test, y_train, y_test, scaler = formatData(allYearAAPL_withEmos) # 建模 history, model = LSTMModelGenerate(X_train, X_test, y_train, y_test) # 損失函數繪圖 drawLossGraph(history, title='LSTM Loss Graph for 2018 All Year AAPL Stock Prices with Emotions', num='8') # 預測 y_predict = model.predict(X_test)[:,0] # 反歸一化 allYearAAPL_withEmos = pd.read_csv('補充數據1925102007/AAPL2018allYearData_withEmos.csv') col_n = allYearAAPL_withEmos.shape[1]-2 inv_yPredict, inv_yTest, inv_yTrain = invTransformMulti(scaler, y_predict, y_test, y_train, col_n) # 繪圖 predictGraph(inv_yTrain, inv_yPredict, inv_yTest, timelabels=allYearAAPL_withEmos['Date'].values, title='Prediction Graph of 2018 All Year AAPL Stock Prices with Emotions', num='9') # 均方誤差 mse = mean_squared_error(inv_yTest, inv_yPredict) print('2018全年含情感特征的AAPL股票數據預測結果的均方誤差(MSE)為 ', mse) 2018全年含情感特征的AAPL股票數據預測結果的均方誤差(MSE)為 1.55267913.9.3 結果分析
模型訓練損失圖:對比Fig2. LSTM Loss Graph for Stock Prices with Emotions和Fig8. LSTM Loss Graph for 2018 All Year AAPL Stock Prices with Emotions,發現使用2018全年AAPL含情感特征的股票數據訓練LSTM模型,在約10次左右epochs時收斂,而部分AAPL含情感特征的股票數據訓練則需要約20次左右epochs才能收斂。表明,隨訓練樣本的增加,LSTM模型使損失函數收斂所需的迭代次數更少,且擬合效果更佳。
預測結果圖:對比Fig7. Prediction Graph of 2018 All Year AAPL Stock Prices和Fig9. Prediction Graph of 2018 All Year AAPL Stock Prices with Emotions(即只含純技術指標的和加入情感特征后的2018全年AAPL股票數據預測結果圖),發現二者差異甚微。但通過二者MSE值不難發現,MSE (2018全年含情感特征的AAPL股票數據) < MSE (2018全年純技術指標AAPL股票數據),表明在總體樣本量擴大,讓評論情感特征數據的時間能夠覆蓋所有股票技術指標的情況下,向純技術指標的股票數據中添加情感特征數據后,能夠增加對股票收盤價close的預測精度。
MSE (2018全年含情感特征的AAPL股票數據) = 1.5526791
MSE (2018全年純技術指標AAPL股票數據) = 1.7402486
4. 結論與總結
本實驗探究了情感結構化特征數據在LSTM股票預測模型中的影響。利用Pandas對所給數據進行預處理(數據載入、清洗與準備、規整、時間序列處理、數據聚合等),確保數據的可用性。再借助NLTK和LM金融詞庫,對非結構化文本信息進行情感分析,并將所得結構化數據融入純技術指標的股票數據中。分析各股票指標的相關性,實現數據降維,提升模型訓練速度。基于Keras的以MSE為誤差評價方法的LSTM模型,分別使用含有情感和不含情感的部分股票數據和2018全年股票數據實現對股票收盤價Close的預測。
實驗結果表明,LSTM模型預測股票收盤價Close時,在訓練樣本量較少的情況下,無論有無情感數據的融入,預測值隨時間的推移嚴重偏離真值,即預測精度較低,而情感數據的融入讓預測值變得更加靈敏,漲跌情況更符合真值,但預測精度有所下降。然而,當訓練樣本充足時,不僅預測精度大幅提升,而且因融入了情感特征數據,使得預測靈敏度適當增加,導致總體預測精度再次增長。
5. 參考文獻
[1] Wes McKinney. 利用Python進行數據分析[M]. 機械工業出版社. 2013
[2] 洪志令, 吳梅紅. 股票大數據挖掘實戰——股票分析篇[M]. 清華大學出版社. 2020
[3] 楊妥, 李萬龍, 鄭山紅. 融合情感分析與SVM_LSTM模型的股票指數預測. 軟件導刊, 2020(8):14-18.
[4] Francesca Lazzeri. Machine Learning for Time Series Forecasting with Python[M]. Wiley. 2020
數據集下載:
百度云- https://pan.baidu.com/s/1tC1AFx0kMHPUGobvqf47pg
華大云盤- https://pan.hqu.edu.cn/share/a474d56c6b6557f7a7fd0e0eb7
密碼- ued8
總結
以上是生活随笔為你收集整理的情感数据对LSTM股票预测模型的影响研究的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用浏览器获取网页模板(HTML+CSS
- 下一篇: Leetcode--397. 整数替换