
Jaringan saraf dalam telah menjadi semakin populer dalam beberapa tahun terakhir, memecahkan masalah yang sebelumnya tidak dapat dipecahkan di banyak bidang dan menunjukkan kemampuannya yang hebat. Dalam prediksi deret waktu, harga jaringan syaraf yang umum digunakan adalah RNN, karena RNN tidak hanya memiliki input data terkini, tetapi juga input data historis. Tentu saja, ketika kita berbicara tentang RNN yang memprediksi harga, kita sering berbicara tentang jenis RNN : LSTM . Artikel ini akan membangun model untuk memprediksi harga Bitcoin berdasarkan pytorch. Meskipun ada banyak informasi relevan di Internet, itu masih belum cukup menyeluruh, dan relatif sedikit orang yang menggunakan pytorch. Tetap perlu menulis artikel. Hasil akhirnya adalah menggunakan harga pembukaan, harga penutupan, harga tertinggi, harga terendah, dan volume transaksi pasar Bitcoin. untuk memprediksi harga penutupan berikutnya. Pengetahuan pribadi saya tentang jaringan saraf rata-rata, dan saya menyambut kritik dan koreksi Anda. Tutorial ini dibuat oleh FMZ, penemu platform perdagangan kuantitatif mata uang digital (www.fmz.com). Selamat bergabung di grup QQ: 863946592 untuk komunikasi.
Contoh prediksi harga terkait: https://yq.aliyun.com/articles/538484 Pengenalan terperinci tentang model RNN: https://zhuanlan.zhihu.com/p/27485750 Memahami input dan output RNN: https://www.zhihu.com/question/41949741/answer/318771336 Tentang pytorch: Dokumentasi resmi https://pytorch.org/docs Cari sendiri informasi lainnya. Selain itu, diperlukan beberapa pengetahuan prasyarat untuk memahami artikel ini, seperti pandas/crawler/pemrosesan data, dll., tetapi tidak masalah jika Anda tidak mengetahuinya.
Parameter LSTM:
Ketika saya pertama kali melihat parameter yang padat ini pada dokumen, reaksi saya adalah:
Saat saya membaca perlahan, saya akhirnya memahaminya.

input_size: Ukuran fitur vektor input x. Jika harga penutupan digunakan untuk memprediksi harga penutupan, maka input_size=1; jika harga penutupan diprediksi oleh harga pembukaan tertinggi dan harga penutupan terendah, maka input_size=4
hidden_size: Ukuran lapisan tersembunyi
num_layers: Jumlah lapisan RNN
batch_first: Jika Benar, dimensi input pertama adalah batch_size. Parameter ini juga sangat membingungkan dan akan dijelaskan secara rinci di bawah ini.
Parameter data masukan:

input: Data masukan spesifik adalah tensor tiga dimensi dengan bentuk spesifik (seq_len, batch, input_size). Di antara mereka, seq_len mengacu pada panjang urutan, yaitu, seberapa panjang data historis yang perlu dipertimbangkan LSTM. Perhatikan bahwa ini hanya mengacu pada format data, bukan struktur internal LSTM. Model LSTM yang sama dapat data input dengan seq_len yang berbeda dan dapat memberikan prediksi. Hasil; batch mengacu pada ukuran batch, yang menunjukkan berapa banyak kelompok data yang berbeda; input_size adalah input_size sebelumnya.
h_0: Keadaan tersembunyi awal, bentuknya (num_layers * num_directions, batch, hidden_size), jika merupakan jaringan dua arah num_directions=2
c_0: Keadaan sel awal, bentuknya sama seperti di atas, dapat dibiarkan tidak ditentukan.
Parameter keluaran:

output: Bentuk keluaran (seq_len, batch, num_directions * hidden_size), perhatikan bahwa ini terkait dengan parameter model batch_first
h_n: h state pada waktu t = seq_len, bentuknya sama dengan h_0
c_n: c state pada waktu t = seq_len, bentuknya sama dengan c_0
Pertama impor paket yang diperlukan
import pandas as pd
import numpy as np
import torch
import torch.nn as nn
import matplotlib.pyplot as plt
Mendefinisikan model LSTM
LSTM = nn.LSTM(input_size=5, hidden_size=10, num_layers=2, batch_first=True)
Mempersiapkan data masukan
x = torch.randn(3,4,5)
# x的值为:
tensor([[[ 0.4657, 1.4398, -0.3479, 0.2685, 1.6903],
[ 1.0738, 0.6283, -1.3682, -0.1002, -1.7200],
[ 0.2836, 0.3013, -0.3373, -0.3271, 0.0375],
[-0.8852, 1.8098, -1.7099, -0.5992, -0.1143]],
[[ 0.6970, 0.6124, -0.1679, 0.8537, -0.1116],
[ 0.1997, -0.1041, -0.4871, 0.8724, 1.2750],
[ 1.9647, -0.3489, 0.7340, 1.3713, 0.3762],
[ 0.4603, -1.6203, -0.6294, -0.1459, -0.0317]],
[[-0.5309, 0.1540, -0.4613, -0.6425, -0.1957],
[-1.9796, -0.1186, -0.2930, -0.2619, -0.4039],
[-0.4453, 0.1987, -1.0775, 1.3212, 1.3577],
[-0.5488, 0.6669, -0.2151, 0.9337, -1.1805]]])
Bentuk x adalah (3,4,5), karena kita mendefinisikanbatch_first=True, saat ini, batch_size adalah 3, sqe_len adalah 4, dan input_size adalah 5. X[[0] mewakili kelompok pertama.
Jika batch_first tidak didefinisikan, nilainya akan menjadi False secara default, dan data direpresentasikan secara berbeda, dengan ukuran batch 4, sqe_len 3, dan input_size 5. Pada saat ini x[[0] mewakili data semua batch pada t=0, dan seterusnya. Saya pribadi merasa bahwa pengaturan ini tidak intuitif, jadi saya menambahkan parameterbatch_first=True.
Konversi data antara keduanya juga sangat nyaman:x.permute(1,0,2)
Masukan dan Keluaran
Bentuk input dan output LSTM mudah membingungkan, dengan bantuan gambar berikut untuk membantu pemahaman:

Sumber: https://www.zhihu.com/question/41949741/answer/318771336
x = torch.randn(3,4,5)
h0 = torch.randn(2, 3, 10)
c0 = torch.randn(2, 3, 10)
output, (hn, cn) = LSTM(x, (h0, c0))
print(output.size()) #在这里思考一下,如果batch_first=False输出的大小会是多少?
print(hn.size())
print(cn.size())
#结果
torch.Size([3, 4, 10])
torch.Size([2, 3, 10])
torch.Size([2, 3, 10])
Amati hasil keluaran yang konsisten dengan penjelasan parameter sebelumnya. Perhatikan bahwa nilai kedua hn.size() adalah 3, yang konsisten dengan ukuran batch_size, yang menunjukkan bahwa tidak ada status peralihan yang disimpan dalam hn, hanya langkah terakhir. Karena jaringan LSTM kita memiliki dua lapisan, output dari lapisan terakhir hn sebenarnya adalah nilai output, dan bentuk outputnya adalah[3, 4, 10], menyimpan hasil semua momen t=0,1,2,3, jadi:
hn[-1][0] == output[0][-1] #第一个batch在hn最后一层的输出等于第一个batch在t=3时output的结果
hn[-1][1] == output[1][-1]
hn[-1][2] == output[2][-1]
Banyak hal yang telah saya katakan sebelumnya hanyalah pendahuluan. Sangat penting untuk memahami input dan output LSTM. Jika tidak, akan mudah membuat kesalahan jika Anda menyalin beberapa kode dari Internet secara acak. Karena kemampuan yang hebat dari LSTM dalam deret waktu, meskipun modelnya salah, Anda bisa mendapatkannya pada akhirnya. Hasil yang bagus.
Akuisisi Data
Data yang digunakan adalah data pasar pasangan perdagangan BTC_USD dari bursa Bitfinex.
import requests
import json
resp = requests.get('https://q.fmz.com/chart/history?symbol=bitfinex.btc_usd&resolution=15&from=0&to=0&from=1525622626&to=1562658565')
data = resp.json()
df = pd.DataFrame(data,columns = ['t','o','h','l','c','v'])
print(df.head(5))
Format datanya adalah sebagai berikut:

Prapemrosesan data
df.index = df['t'] # index设为时间戳
df = (df-df.mean())/df.std() # 数据的标准化,否则模型的Loss会非常大,不利于收敛
df['n'] = df['c'].shift(-1) # n为下一个周期的收盘价,是我们预测的目标
df = df.dropna()
df = df.astype(np.float32) # 改变下数据格式适应pytorch
Metode standarisasi data sangat kasar dan akan ada beberapa masalah. Ini hanya untuk demonstrasi. Anda dapat menggunakan standarisasi data seperti yield.
Mempersiapkan data pelatihan
seq_len = 10 # 输入10个周期的数据
train_size = 800 # 训练集batch_size
def create_dataset(data, seq_len):
dataX, dataY=[], []
for i in range(0,len(data)-seq_len, seq_len):
dataX.append(data[['o','h','l','c','v']][i:i+seq_len].values)
dataY.append(data['n'][i:i+seq_len].values)
return np.array(dataX), np.array(dataY)
data_X, data_Y = create_dataset(df, seq_len)
train_x = torch.from_numpy(data_X[:train_size].reshape(-1,seq_len,5)) #变化形状,-1代表的值会自动计算
train_y = torch.from_numpy(data_Y[:train_size].reshape(-1,seq_len,1))
Bentuk akhir dari train_x dan train_y adalah: torch.Size([800, 10, 5]), torch.Size([800, 10, 1]). Karena model kami memprediksi harga penutupan periode berikutnya berdasarkan data dari 10 periode, secara teoritis, 800 batch hanya memerlukan 800 harga penutupan yang diprediksi. Namun train_y memiliki 10 data dalam setiap batch. Faktanya, hasil antara dari setiap prediksi batch dipertahankan, bukan hanya yang terakhir. Saat menghitung Kerugian akhir, semua 10 hasil prediksi dapat diperhitungkan dan dibandingkan dengan nilai aktual di train_y. Secara teoritis, hanya mungkin untuk menghitung Kerugian hasil prediksi terakhir. Saya menggambar diagram kasar untuk mengilustrasikan masalah ini. Karena model LSTM sebenarnya tidak berisi parameter seq_len, model tersebut dapat diterapkan pada panjang yang berbeda, dan hasil prediksi antara juga bermakna, jadi saya cenderung menggabungkan perhitungan Loss.

Perlu diperhatikan bahwa saat menyiapkan data pelatihan, pergerakan jendela akan tersendat-sendat, dan data yang telah digunakan tidak lagi digunakan. Tentu saja, jendela juga dapat dipindahkan satu per satu, sehingga set pelatihan yang diperoleh akan jauh lebih besar. . Tetapi saya merasa data batch yang berdekatan terlalu berulang, jadi saya mengadopsi metode saat ini.

Model akhir adalah sebagai berikut, yang mencakup LSTM dua lapis dan lapisan Linear.
class LSTM(nn.Module):
def __init__(self, input_size, hidden_size, output_size=1, num_layers=2):
super(LSTM, self).__init__()
self.rnn = nn.LSTM(input_size,hidden_size,num_layers,batch_first=True)
self.reg = nn.Linear(hidden_size,output_size) # 线性层,把LSTM的结果输出成一个值
def forward(self, x):
x, _ = self.rnn(x) # 如果不理解前向传播中数据维度的变化,可单独调试
x = self.reg(x)
return x
net = LSTM(5, 10) # input_size为5,代表了高开低收和交易量. 隐含层为10.
Akhirnya mulai pelatihan, kodenya adalah sebagai berikut:
criterion = nn.MSELoss() # 使用了简单的均方差损失函数
optimizer = torch.optim.Adam(net.parameters(),lr=0.01) # 优化函数,lr可调
for epoch in range(600): # 由于速度很快,这里的epoch多一些
out = net(train_x) # 由于数据量很小, 直接拿全量数据计算
loss = criterion(out, train_y)
optimizer.zero_grad()
loss.backward() # 反向传播损失
optimizer.step() # 更新参数
print('Epoch: {:<3}, Loss:{:.6f}'.format(epoch+1, loss.item()))
Hasil pelatihannya adalah sebagai berikut:

Nilai prediksi modelnya adalah:
p = net(torch.from_numpy(data_X))[:,-1,0] # 这里只取最后一个预测值作为比较
plt.figure(figsize=(12,8))
plt.plot(p.data.numpy(), label= 'predict')
plt.plot(data_Y[:,-1], label = 'real')
plt.legend()
plt.show()

Seperti yang terlihat pada gambar, tingkat kecocokan data pelatihan (sebelum 800) sangat tinggi, tetapi harga Bitcoin telah naik ke titik tertinggi baru kemudian, dan model belum melihat data ini, jadi prediksinya adalah tidak dapat bekerja dengan baik. Ini juga menunjukkan adanya masalah dalam standardisasi data sebelumnya.
Meskipun harga yang diprediksi mungkin tidak akurat, seberapa akurat prediksi kenaikan dan penurunannya? Lihatlah bagian data prediksi:
r = data_Y[:,-1][800:1000]
y = p.data.numpy()[800:1000]
r_change = np.array([1 if i > 0 else 0 for i in r[1:200] - r[:199]])
y_change = np.array([1 if i > 0 else 0 for i in y[1:200] - r[:199]])
print((r_change == y_change).sum()/float(len(r_change)))
Akurasi prediksi naik turunnya mencapai 81,4%, melebihi ekspektasi saya. Saya tidak tahu apakah saya membuat kesalahan di suatu tempat.
Tentu saja, model ini tidak memiliki nilai riil, tetapi sederhana dan mudah dipahami. Gunakan saja ini sebagai titik awal. Akan ada lebih banyak kursus pengantar tentang penerapan jaringan saraf dalam kuantifikasi mata uang digital.