作為棋盤遊戲,圍棋已有2000多年的曆史。長期以來,它一直被認為是美與藝術的創造,因為其規則很簡單,但對弈非常複雜,并且曾認為在未來幾十年中能經受住人工智能的挑戰。然而,當時間來到2016年3月9日至15日,在南韓首爾進行的南韓圍棋九段棋手李世石與人工智能圍棋程式阿爾法圍棋AlphaGo之間的五番棋比賽,AlphaGo以4-1戰勝了這位世界頂級圍棋棋手,這是人工智能裡程碑式的成就。
AlphaGo是使用基于數百萬人類專家遊戲的訓練資料集以及其他資源進行監督學習的。AlphaGo的下一個疊代版本AlphaGo Zero,完全跳過了這種方法,僅依靠強化學習和自我對弈,将不同代且訓練有素的基于神經網絡的人工智能體放在一起互相競争。Silver等人在其2017年發表的文章中介紹了AlphaGo Zero的詳細資訊。在摘要中, 研究人員總結道:
AlphaGo成了自己的老師:訓練神經網絡來預測自己的走法選擇以及遊戲的獲勝者。該神經網絡提高了樹搜尋的強度,進而能在下一次疊代中産生更高品質的走 法選擇和更強的自我對弈。從一無所知開始,新程式 AlphaGo Zero 取得了超人的表現,以100–0戰勝了之前釋出的擊敗了李世石的AlphaGo。
由此可見,在行動政策中将強化學習與神經網絡相結合的優越性。下面我們來簡單學習下“強化學習”。
基本概念
- 環境
環境定義了目前的問題,可以是要玩的計算機遊戲或要進行交易的金融市場。
- 狀态
狀态包含描述環境目前狀态的所有相關參數。在計算機遊戲中,這可能是整個螢幕及其 像素。在金融市場中,這可能包括目前和曆史價格水準或金融名額,比如移動平均線、 宏觀經濟變量等。
- 智能體
智能體這個詞包含了與環境互動并從這些互動中學習的強化學習算法的所有元素。在遊 戲環境中,智能體可能代表玩遊戲的玩家。在金融環境中,智能體可以代表在市場中進 行交易的交易者。
- 動作
智能體可以從一組(有限的)被允許的動作中選擇一個動作。在計算機遊戲中,被允許 的動作可能是向左或向右移動,而在金融市場中,被允許的動作可能是做多或做空。
- 步驟
給定智能體的動作,環境狀态會被更新,這樣的更新通常被稱為一個步驟。步驟的概念 可以包含兩個步驟之間的相同或者不同的時間間隔。雖然在計算機遊戲中,與遊戲環境 的實時互動是通過相當短且相同的時間間隔(“遊戲時鐘”)來模拟的,但諸如與金融市 場環境互動的交易機器人則可以在更長且不同的時間間隔内采取動作。
- 獎勵
根據智能體選擇的動作,對其實行獎勵(或懲罰)。對于計算機遊戲,積分是一種典型 的獎勵。在金融環境中,利潤(或虧損)是一種标準的獎勵(或懲罰)。
- 目标
目标是指智能體試圖最大化的内容。在計算機遊戲中,這通常是智能體達到的分數。對 于金融交易機器人,這可能是累積的交易利潤。
- 政策
政策定義了智能體在給定環境狀态下所采取的動作。給定計算機遊戲的特定狀态(由構 成目前場景的所有像素表示),政策可能會指定智能體選擇“向右移動”作為動作。觀 察到連續 3 個價格上漲的交易機器人可能會根據其政策決定做空市場。
- 回合
回合是從環境的初始狀态到成功或可預見的失敗的一組步驟。在遊戲中,這是從遊戲開 始到輸或赢為止。以金融界為例,這就是從年初到年底,或者到破産。
接下來我們在執行個體中逐漸說明上述概念。
OpenAI Gym
OpenAI是一個緻力于促進人工智能研究,特别是強化學習研究的組織。 OpenAI開發并開源了一套環境,稱為 OpenAI Gym,允許通過标準化API訓練RL智能體。在衆多環境中,有模拟經典強化學習問題的CartPole環境(或遊戲),即把一根杆子直立在推車上,目的是通過左右移動推車來學習平衡杆子的政策。環境狀态由4個參數表示, 包括以下實體測量值:推車位置、推車速度、極角和極角速度(尖端)。圖1描述了一個可視化的環境。
圖1 OpenAI Gym的CartPole環境
以下代碼執行個體化CartPole的環境對象,觀察空間是環境狀态的模型。
env = gym.make('CartPole-v1', render_mode="rgb_array")
env.action_space.seed(100)
# 初始狀态:推車位置、推車速度、極角和極角速度
state, _ = env.reset()
state
# Out:
# array([ 0.0438, -0.0123, 0.0364, -0.0128], dtype=float32)
隻要 done=False,智能體就還在遊戲中,并且可以選擇另一個動作。當智能體達到總計500步(CartPole-v0版本是200步)或總獎勵達到500(每步獎勵 1.0)時,即取得成功。當推車上的杆子到達一定角度導緻杆子從推車上掉下來時,故障被觀測到,在這種情況下,傳回done=True。簡單智能體是遵循完全随機政策的智能體:無論觀察到什麼狀态,智能體都會選擇一個随機動作,如下面的代碼實作所示。在這種情況下,智能體可以走的步數僅取決于它的幸運程度,其并未以更新政策的形式進行學習。
env.reset()
for e in range(500):
# 随機動作政策
a = env.action_space.sample()
# 向前一步
state, reward, done, truncated, info = env.step(a)
print(f'step={e:2d} | state={state} | action={a} | reward={reward}')
# 少于200步則失敗
if done and (e + 1) < 200:
print('*** FAILED ***')
break
# Out:
# step= 1 | state=[ 0.0096 -0.1469 -0.0307 0.2928] | action=0 | reward=1.0
# step= 2 | state=[ 0.0066 -0.3415 -0.0249 0.5757] | action=0 | reward=1.0
# step= 3 | state=[-0.0002 -0.5363 -0.0133 0.8604] | action=0 | reward=1.0
# step= 4 | state=[-0.0109 -0.7312 0.0039 1.1489] | action=0 | reward=1.0
# step= 5 | state=[-0.0255 -0.9264 0.0268 1.4428] | action=0 | reward=1.0
# step= 6 | state=[-0.0441 -1.1218 0.0557 1.7437] | action=0 | reward=1.0
# step= 7 | state=[-0.0665 -1.3175 0.0906 2.0532] | action=0 | reward=1.0
# step= 8 | state=[-0.0928 -1.5135 0.1316 2.3725] | action=0 | reward=1.0
# step= 9 | state=[-0.1231 -1.7095 0.1791 2.7025] | action=0 | reward=1.0
# step=10 | state=[-0.1573 -1.9054 0.2331 3.0441] | action=0 | reward=1.0
# *** FAILED ***
done
# Out:
# True
在監督學習中,假設訓練資料集、驗證資料集和測試資料集在訓練開始之前已經存在,而在強化學習中,智能體通過與環境互動來生成自己的資料。在許多情況下(比如在遊戲中),這是一個巨大的簡化。考慮一下國際象棋遊戲:一個 RL智能體可以通過與另一個國際象棋引擎或另一個版本的自身對戰,而不是将數千個人類曆史上所生成的國際象棋棋譜加載到計算機中。
蒙特卡羅智能體
CartPole問題不一定非要使用成熟的強化學習方法和一些神經網絡來解決,下面是基于蒙特卡羅模拟的簡單解決方案,并使用降維的特定政策。在這種情況下,定義環境狀态的4個參數通過線性組合被壓縮為了單個實值參數。
np.random.seed(100)
weights = np.random.random(4) * 2 - 1
# weights
print(f'weights: {weights}')
# 環境的初始狀态
state, _ = env.reset()
print(f'state: {state}')
# 狀态和權重的點積
s = np.dot(state, weights)
print(f's: {s}')
# Out:
# weights: [ 0.0868 -0.4433 -0.151 0.6896]
# state: [-0.0465 -0.0319 -0.0016 -0.0308]
# s: -0.010908021653158767
接下來可以使用此政策玩一回合CartPole遊戲。鑒于所應用的權重的随機性,通常結果并不比上一節的随機動作政策的結果好。
def run_episode(env, weights):
state = env.reset()[0]
treward = 0
for _ in range(500):
s = np.dot(state, weights)
# 根據單個狀态參數s的符号來選擇政策
a = 0 if s < 0 else 1
state, reward, done, truncated, info = env.step(a)
treward += reward
if done:
break
return treward
# run_episode(env, weights)
是以,可以應用蒙特卡羅模拟來測試大量不同的權重,檢查它們是成功還是失敗,然後選擇産生成功的權重。
def set_seeds(seed=100):
random.seed(seed)
np.random.seed(seed)
set_seeds()
num_episodes = 1000
besttreward = 0
for e in range(1, num_episodes + 1):
# 随機權重
weights = np.random.rand(4) * 2 - 1
# 這些權重的總獎勵
treward = run_episode(env, weights)
# 觀察是否有改善
if treward > besttreward:
# 替換最佳總獎勵
besttreward = treward
# 替換最佳權重
bestweights = weights
if treward == 200:
print(f'SUCCESS | episode={e}')
break
print(f'UPDATE | episode={e}')
# Out:
# UPDATE | episode=1
# UPDATE | episode=2
# UPDATE | episode=13
# UPDATE | episode=35
如果連續 100 回合的平均總獎勵為195或更高,則認為CartPole問題已被智能體解決,如以下代碼所示。
res = []
for _ in range(100):
treward = run_episode(env, bestweights)
res.append(treward)
res[:10]
# Out:
# [500.0, 500.0, 500.0, 500.0, 500.0, 500.0, 500.0, 500.0, 500.0, 500.0]
這是一個用來與其他更複雜的方法做比較的強有力的基準。
神經網絡智能體
CartPole遊戲也可以被轉換為一個分類問題:環境狀态由4個特征值組成,對應每組給定特征值的正确操作是需要預測的标簽。通過與環境互動,神經網絡智能體可以收集由特征值和标簽組合組成的資料集。給定這個不斷增長的資料集,可以訓練神經網絡來學 習給定環境狀态的正确動作。在這種情況下,神經網絡代表政策,智能體會根據新體驗更新政策。
首先是一些庫的引用。
import tensorflow as tf
from keras.layers import Dense, Dropout
from keras.models import Sequential
from keras.optimizers import adam_v2, rmsprop_v2
from sklearn.metrics import accuracy_score
def set_seeds(seed=100):
random.seed(seed)
np.random.seed(seed)
tf.random.set_seed(seed)
env.action_space.seed(100)
然後是一個NNAgent類,它結合了智能體的主要元素:政策的神經網絡模型、根據政策選擇動作、更新政策(訓練神經網絡),以及多個回合的學習過程本身。智能體同時使用探索和利用來選擇一個動作。探索是指随機動作,獨立于目前政策。利用是指從目前政策派生的動作。這麼做是因為某種程度的探索可以確定獲得更豐富的經驗,進而使智能體的學習得到改善。
class NNAgent:
def __init__(self):
# 最高總獎勵
self.max = 0
self.scores = list()
self.memory = list()
self.model = self._build_model()
# 政策的 DNN 分類模型
def _build_model(self):
model = Sequential()
model.add(Dense(24, input_dim=4,
activation='relu'))
model.add(Dense(1, activation='sigmoid'))
model.compile(loss='binary_crossentropy', optimizer=rmsprop_v2.RMSprop(learning_rate=0.001))
return model
# 選擇動作的方法(探索和利用)
def act(self, state):
if random.random() <= 0.5:
return env.action_space.sample()
action = np.where(self.model.predict(
state, batch_size=None)[0, 0] > 0.5, 1, 0)
return action
# 更新政策的方法(訓練神經網絡)
def train_model(self, state, action):
self.model.fit(state, np.array([action,]),
epochs=1, verbose=False)
# 從與環境的互動中學習的方法
def learn(self, episodes):
for e in range(1, episodes + 1):
state = env.reset()[0]
for _ in range(500):
state = np.reshape(state, [1, 4])
action = self.act(state)
next_state, reward, done, truncated, info = env.step(action)
if done:
score = _ + 1
self.scores.append(score)
self.max = max(score, self.max)
print('episode: {:4d}/{} | score: {:3d} | max: {:3d}'
.format(e, episodes, score, self.max), end='\r')
break
self.memory.append((state, action))
self.train_model(state, action)
state = next_state
神經網絡智能體沒有解決上述配置下的問題,最大總獎勵甚至一次也沒有達到200。
set_seeds(100)
agent = NNAgent()
episodes = 500
agent.learn(episodes)
# Out:
# episode: 500/500 | score: 12 | max: 45
# 所有回合的平均總獎勵
sum(agent.scores) / len(agent.scores)
# Out:
# 13.89
這種方法似乎缺少一些東西,而其中一個主要的缺失元素是超越目前狀态和要選擇的動作的想法。目前為止所實作的方法無論如何都沒有考慮到隻有當智能體連續存活200步時才 能取得成功。簡單地說,智能體會避免采取錯誤的動作,但沒有學會赢得比賽。
分析收集到的狀态(特征)和動作(标簽)的曆史資料表明,神經網絡的準确率達到了75%左右。然而,這并沒有轉化為之前看到的獲勝政策。
# 所有回合的特征(狀态)
f = np.array([m[0][0] for m in agent.memory])
# 所有回合的标簽(動作)
l = np.array([m[1] for m in agent.memory])
accuracy_score(np.where(agent.model.predict(f) > 0.5, 1, 0), l)
# Out:
# 0.7453840186190845
DQL智能體
Q學習是一種強化學習算法,除了會考慮來自動作的即時獎勵,還會考慮延遲獎勵。該算法歸功于Watkins(1989)以及Watkins 和 Dayan(1992),并在 Sutton 和 Barto(2018)的第6章中有詳細解釋。Q學習解決了神經網絡智能體遇到的超越下一個立即獎勵的問題。
該算法的工作原理大緻如下:有一個動作–價值政策Q,它為每個狀态和動作的組合配置設定一個值。值越高,從智能體的角度來看動作越好。如果智能體使用政策Q選擇一個動作, 則它會選擇具有最高值的動作。
那麼一個動作的價值是如何得出的呢?一個動作的價值由它的直接獎勵和下一狀态下最優動作的折現值組成,以下是正式表達。
這裡,是步驟(時間)t的狀态, 是狀态采取的動作,是動作的直接獎勵,是折扣因子, 是給定目前政策 Q 的最優動作下的最大延遲獎勵。 a在一個隻有有限數量的可能狀态的簡單環境中,Q 可以用一張表格表示,其中列出了每個狀态–動作組合的相應值。然而,在更有趣或更複雜的設定中(如CartPole環境),狀态數量過多,無法通過窮盡的方式寫出Q,是以,Q通常被了解為一個函數。
這就是神經網絡發揮作用的地方。在現實的設定和環境中,函數Q的封閉形式可能不存在,或者利用動态規劃方法很難推導。是以Q學習算法通常僅以近似值為目标。神經網絡具有通用逼近能力,是完成Q函數逼近的自然選擇。
Q學習的另一個關鍵要素是重放。DQL智能體會重放許多經驗(狀态–動作組合)以定期更新政策函數Q,這可以大大提高學習效果。此外,下面介紹的DQL智能體(DQLAgent) 于學習過程中在探索和利用之間交替。交替會以系統的方式進行,因為智能體僅從探索 開始(一開始它不可能學到任何東西),然後會緩慢但穩定地降低探索率 ϵ 直到達到最低水準。
from collections import deque
from keras.optimizers import adam_v2, rmsprop_v2
class DQLAgent:
def __init__(self, gamma=0.95, hu=24, opt=adam_v2.Adam, lr=0.001, finish=False):
self.finish = finish
# 初始探索率
self.epsilon = 1.0
# 最小探索率
self.epsilon_min = 0.01
# 探索率的衰減率
self.epsilon_decay = 0.995
# 延遲獎勵的折扣因子
self.gamma = gamma
# 重放的批次大小
self.batch_size = 32
self.max_treward = 0
self.averages = list()
# 有限曆史的雙端隊列 deque 集合
self.memory = deque(maxlen=2000)
self.osn = env.observation_space.shape[0]
self.model = self._build_model(hu, opt, lr)
def _build_model(self, hu, opt, lr):
model = Sequential()
model.add(Dense(hu, input_dim=self.osn,
activation='relu'))
model.add(Dense(hu, activation='relu'))
model.add(Dense(env.action_space.n, activation='linear'))
model.compile(loss='mse', optimizer=opt(learning_rate=lr))
return model
def act(self, state):
if random.random() <= self.epsilon:
return env.action_space.sample()
action = self.model.predict(state)[0]
return np.argmax(action)
def replay(self):
# 随機選擇曆史批次進行回放。
batch = random.sample(self.memory, self.batch_size)
for state, action, reward, next_state, done in batch:
if not done:
reward += self.gamma * np.amax(
self.model.predict(next_state)[0]) # 狀态–動作對的Q值
target = self.model.predict(state)
target[0, action] = reward
# 為新的動作–價值對更新神經網絡
self.model.fit(state, target, epochs=1,
verbose=False)
if self.epsilon > self.epsilon_min:
# 更新探索率
self.epsilon *= self.epsilon_decay
def learn(self, episodes):
trewards = []
for e in range(1, episodes + 1):
state, _ = env.reset()
state = np.reshape(state, [1, self.osn])
for _ in range(5000):
action = self.act(state)
# next_state, reward, done, info = env.step(action)
next_state, reward, done, truncated, info = env.step(action)
next_state = np.reshape(next_state,
[1, self.osn])
# 存儲新資料
self.memory.append([state, action, reward,
next_state, done])
state = next_state
if done:
treward = _ + 1
trewards.append(treward)
av = sum(trewards[-25:]) / 25
self.averages.append(av)
self.max_treward = max(self.max_treward, treward)
templ = 'episode: {:4d}/{} | treward: {:4d} | '
templ += 'av: {:6.1f} | max: {:4d}'
print(templ.format(e, episodes, treward, av,
self.max_treward), end='\r')
break
if av > 195 and self.finish:
print()
break
if len(self.memory) > self.batch_size:
# 根據過去的經驗重放以更新政策
self.replay()
def test(self, episodes):
trewards = []
for e in range(1, episodes + 1):
state = env.reset()
for _ in range(5001):
state = np.reshape(state, [1, self.osn])
action = np.argmax(self.model.predict(state)[0])
next_state, reward, done, truncated, info = env.step(action)
state = next_state
if done:
treward = _ + 1
trewards.append(treward)
print('episode: {:4d}/{} | treward: {:4d}'
.format(e, episodes, treward), end='\r')
break
return trewards
DQL智能體如何執行?如以下代碼所示,它達到了CartPole的獲勝狀态,總獎勵為200。 圖2顯示了分數的移動平均線以及它是如何随時間增加的,盡管不是單調增加。相反, 智能體的性能有時會顯著下降。除此之外,一直在進行的探索會導緻随機操作,這不一定會導緻總獎勵方面的良好結果,但可能會為更新政策網絡帶來有益的經驗。
episodes = 1000
set_seeds(100)
agent = DQLAgent(finish=True)
agent.learn(episodes)
# Out:
# episode: 510/1000 | treward: 224 | av: 199.4 | max: 740
plt.figure(figsize=(10, 6))
x = range(len(agent.averages))
y = np.polyval(np.polyfit(x, agent.averages, deg=3), x)
plt.plot(agent.averages, label='moving average')
plt.plot(x, y, 'r--', label='trend')
plt.xlabel('episodes')
plt.ylabel('total reward')
plt.legend();
圖2 CartPole的DQLAgent平均總獎勵
DQL智能體是否解決了CartPole問題?在現有的遊戲設定下,考慮到OpenAI Gym對成功的定義,确實解決了。
trewards = agent.test(100)
# Out:
# episode: 100/100 | treward: 468
sum(trewards) / len(trewards)
# Out:
# 532.79
簡單的金融沙箱
為了将Q學習方法遷移至金融領域,下面展示了一個模拟OpenAI Gym環境的類,但其僅适用于以金融時間序列資料為代表的金融市場。這裡的思路是,類似于CartPole環境,4個曆史價格代表金融市場的狀态,當給定一個狀态時,智能體可以決定是做多還是做空。 在這種情況下,兩個環境是可比較的,因為每一個狀态都是由4個參數給出,并且智能體可以采取兩種不同的動作。
為了模仿 OpenAI Gym API,需要兩個輔助類,一個用于觀察空間,一個用于動作空間。
class observation_space:
def __init__(self, n):
self.shape = (n,)
class action_space:
def __init__(self, n):
self.n = n
def seed(self, seed):
pass
def sample(self):
return random.randint(0, self.n - 1)
以下代碼定義了Finance類,該類提取了許多交易品種的日終曆史價格。該類的主要方法是 .reset() 和 .step(),其中,.step()方法會檢查是否采取了正确的動作,相應地定義獎勵,并檢查成功或失敗。當智能體能夠通過整個資料集正确交易時,就取得了成功。當然,這可以有不同的定義(例如,定義當智能體成功交易1000步時就取得了成功)。失敗被定義為準确率小于 50%(總獎勵除以總步數)。然而,這僅在一定數量的步驟之後進行檢查,以避免該名額的初始方差過高。
class Finance:
url = 'http://hilpisch.com/aiif_eikon_eod_data.csv'
def __init__(self, symbol, features):
self.symbol = symbol
self.features = features
self.observation_space = observation_space(4)
self.osn = self.observation_space.shape[0]
self.action_space = action_space(2)
# 定義所需的最低準确率
self.min_accuracy = 0.475
self._get_data()
self._prepare_data()
def _get_data(self):
self.raw = pd.read_csv(self.url, index_col=0,
parse_dates=True).dropna()
def _prepare_data(self):
self.data = pd.DataFrame(self.raw[self.symbol])
self.data['r'] = np.log(self.data / self.data.shift(1))
self.data.dropna(inplace=True)
self.data = (self.data - self.data.mean()) / self.data.std()
self.data['d'] = np.where(self.data['r'] > 0, 1, 0)
def _get_state(self):
# 選擇用于定義金融市場狀态的資料
return self.data[self.features].iloc[self.bar - self.osn:self.bar].values
def seed(self, seed=None):
pass
# 将環境重置為其初始值
def reset(self):
self.treward = 0
self.accuracy = 0
self.bar = self.osn
state = self.data[self.features].iloc[
self.bar - self.osn:self.bar]
return state.values
def step(self, action):
# 檢查智能體是否選擇了正确的動作(成功交易)
correct = action == self.data['d'].iloc[self.bar]
# 定義智能體收到的獎勵
reward = 1 if correct else 0
# 将獎勵添加到總獎勵中
self.treward += reward
# 使環境向前運作一步
self.bar += 1
# 計算給定所有步驟(交易)的成功的動作(交易)的準确率
self.accuracy = self.treward / (self.bar - self.osn)
# 如果智能體到達資料集的末尾,則成功
if self.bar >= len(self.data):
done = True
elif reward == 1:
# 如果智能體采取了正确的動作,那麼它可以繼續前進
done = False
elif (self.accuracy < self.min_accuracy and self.bar > self.osn + 10):
# 如果在一些初始步驟之後,準确率下降到最低水準以下,則該回合結束(失敗)
done = True
else:
# 對于其餘情況,智能體可以繼續前進
done = False
state = self._get_state()
info = {}
return state, reward, done, info
Finance 類的執行個體表現得很像OpenAI Gym的環境。特别是,在這種基本情況下,該執行個體表現得與 CartPole 環境完全一樣。
# 指定用于定義代表狀态資料的交易标的代号和特征類型(交易标的代号或對數收益率)
env = Finance('EUR=', 'EUR=')
env.reset()
# Out:
# array([1.819 , 1.8579, 1.7749, 1.8579])
a = env.action_space.sample()
a
# Out:
# 0
env.step(a)
# Out:
# (array([1.8579, 1.7749, 1.8579, 1.947 ]), 0, False, {})
雖然為CartPole遊戲開發的DQLAgent能學習在金融市場中進行的交易,但結果并不“驚豔”(參見圖3)。
set_seeds(100)
agent = DQLAgent(gamma=0.5, opt=rmsprop_v2.RMSprop)
episodes = 1000
agent.learn(episodes)
# Out:
# episode: 1000/1000 | treward: 2511 | av: 1821.2 | max: 2511
agent.test(3)
# Out:
# episode: 3/3 | treward: 2511
# [2511, 2511, 2511]
plt.figure(figsize=(10, 6))
x = range(len(agent.averages))
y = np.polyval(np.polyfit(x, agent.averages, deg=3), x)
plt.plot(agent.averages, label='moving average')
plt.plot(x, y, 'r--', label='regression')
plt.xlabel('episodes')
plt.ylabel('total reward')
plt.legend();
圖3 運作于Finance環境的DQLAgent的平均總獎勵
通過模拟OpenAI Gym環境的API,我們将DQL智能體應用于金融市場環境,無須對智能體本身進行任何更改。盡管智能體在這種新環境中的表現可能并不優異,但它說明了強化學習方法是相當通用的。