深度學習中使用TensorFlow或Pytorch框架時到底是應該使用CPU還是GPU來進行運算???


本文實驗環境為Python3.7, TensorFlow-gpu=1.14, CPU為i7-9700k,鎖頻4.9Ghz, GPU為2060super顯卡

 

 

 

==========================

 

機器學習按照不同的分類標准可以有不同的分類方式,這里我們將深度學習按照感知學習和決策控制學習可以分為兩類。感知學習類的比較有名的就是圖像識別,語言識別等,而決策類的一般就是指強化學習了。

本文要講的事情其實對於做感知學習的人來說是沒有多少意義的,因為使用框架來搞深度學習首選的就是GPU,這基本上可以被認為是唯一選擇,當然這里說的GPU是指計算能力還過得去的顯卡,現在這個時間來說的話怎么你得是RTX2060,2070,2080TI,3060ti,3070,3080,3090的顯卡,你要是非拿好幾年前的老卡來說就沒有比較價值了。換句話說就是搞感知的就是有較新幾代的顯卡那就首選顯卡。

但是本文的意義又在哪呢?本文比較適用的對象主要就是那些搞決策控制方向的機器學習研究的人。

 

為啥搞感知的人一定首選顯卡來做計算,而做決策學習的就要先研究研究我是使用顯卡還是CPU來做計算呢?

首先,我們看下感知學習和決策學習在運算過程中有哪些不同:

1.數據獲得的途徑不同。感知學習在運算前或者說在訓練模型之前我們是已經獲得到數據集的,訓練數據是已知已有可獲得的,而在決策學習中我們在訓練模型之前是無法獲得訓練數據的。決策學習(強化學習)的訓練數據是在訓練過程中動態生成的,該生成過程往往需要和游戲仿真器等外部環境進行交互,而交互過程目前來看全部都是需要使用CPU來進行的,總的來說決策學習的訓練數據需要動態的通過CPU進行計算來獲得,因此決策學習的基本訓練過程可以抽象的理解為:

CPU計算獲得訓練數據+通過CPU或GPU來訓練模型

如果我們在決策學習中每一次迭代(每次更新模型)前獲得的訓練數據較少,而構建的神經網絡模型層數較少或過於簡單就會導致使用CPU或GPU來更新一次模型所需時間差不多,甚至會出現用CPU更新模型的速度快於GPU更新網絡模型。而這時又因為我們是通過CPU來獲得訓練數據的,如果更新一次模型前只獲得少量數據花費較少的時間,而訓練模型是在GPU上,那么將數據從CPU計算時存儲的內存導入到GPU計算時存儲數據的顯存所花時間就會和數據生成的時間與模型更新的時間相當,而這樣就會導致使用gpu更新網絡模型下的整體運算時間高於使用cpu更新網絡模型下的整體運算時間。這個時候我們往往可以通過使用CPU來運行TensorFlow等框架進行提速,而不是使用GPU。

 

2. 神經網絡復雜性不同。感知學習的神經網絡往往采用較復雜的神經網絡架構,或者說網絡層數較多,而決策學習采用的神經網絡架構較為簡單。同樣的訓練數據在使用較復雜神經網絡進行訓練所花費的時間要高於使用較簡單神經網絡訓練的時間。正因為決策學習訓練網絡花費的時間較少使用CPU和GPU來訓練網絡花費時間差不多,又加上數據生成的時間也往往較少(一般更新一次網絡前只生成一個數據),這時如果用GPU訓練網絡而從CPU生成的數據傳輸到GPU所花費的時間就會和數據生成和網絡訓練的時間相當,因此該情況下我們使用CPU來訓練網絡會比GPU訓練網絡更加高效。

 

 

==============================================

 

 

 

 

 

--------------------------------------------------------------------

 

===================================================

從決策學習的訓練過程可以看到一次訓練可以分為A,B,C三個操作,其中A、B操作為數據生成部分,C操作為網絡訓練部分。A操作可以在CPU上運行也可以在GPU上運行,而B操作必須在CPU上運行,而C操作也是既可以在CPU上運行也可以在GPU上運行。

一般情況下,A、B、C操作都是串行的,由於B操作必須在CPU上進行,所以A操作和C操作如果也是在CPU上進行則不需要進行數據切換。其中A操作是使用神經網絡預測下一步動作,C操作是使用神經網絡根據狀態,動作,獎勵等信息進行神經網絡訓練。A操作一般是傳給神經網絡一個圖片然后獲得一個動作,C操作一般是傳給神經網絡batch_size個數據進行網絡訓練。

 

 

 這里我們單獨把A操作拿出來進行運算,分別看下A操作在CPU上和在GPU上時A操作的運算效率:

運行代碼:

import tensorflow as tf
import numpy as np
import time

flags = tf.app.flags
flags.DEFINE_integer('D', 4, '輸入層維度')
flags.DEFINE_integer('H', 50, '隱藏層維度')
flags.DEFINE_float('learning_rate', 1e-4, '學習率')
config = flags.FLAGS


"""
這個module是對神經網絡的定義
"""
import tensorflow as tf


class Policy_Net(object):
    def __init__(self, config):
        self.learning_rate = config.learning_rate  # 1e-2
        self.D = config.D  # 4 輸入向量維度
        self.H = config.H  # 50 隱藏層維度
        # 設置新的graph
        self.graph = tf.Graph()
        # 設置顯存的上限大小
        gpu_options = tf.GPUOptions(allow_growth=True)  # 按照計算需求動態申請內存
        # 設置新的session
        self.sess = tf.Session(graph=self.graph, config=tf.ConfigProto(gpu_options=gpu_options))

        self.net_build()  # 構建網絡
        self.sess.run(self.op_init)  # 全局變量初始化

    def net_build(self):  # 構建網絡
        with self.graph.as_default():
            observations = tf.placeholder(tf.float32, [None, self.D], name="input_states")  # 傳入的觀測到的狀態tensor
            actions = tf.placeholder(tf.float32, [None, 1], name="input_actions")
            advantages = tf.placeholder(tf.float32, [None, 1], name="reward_signal")

            # 根據策略網絡計算出選動作action_0的概率probability, action_1的概率為1-probability
            w1 = tf.get_variable("w1", shape=[self.D, self.H],
                                 initializer=tf.contrib.layers.xavier_initializer())
            b1 = tf.get_variable("b1", shape=[self.H], initializer=tf.constant_initializer(0.0))
            layer1 = tf.nn.relu(tf.add(tf.matmul(observations, w1), b1))
            w2 = tf.get_variable("w2", shape=[self.H, 1],
                                 initializer=tf.contrib.layers.xavier_initializer())
            b2 = tf.get_variable("b2", shape=[1], initializer=tf.constant_initializer(0.0))
            score = tf.add(tf.matmul(layer1, w2), b2)
            """ probability為選擇action=2 的概率, action=1的選擇概率為 1-probability """
            probability = tf.nn.sigmoid(score)  # 動作0的actions=0, 動作1的actions=1

            # advantages 為選擇動作actions后所得到的累計折扣獎勵
            loglik = tf.log(actions * (actions - probability) + (1 - actions) * (actions + probability))
            # loss為一個episode內所有observation,actions,advantages得到的損失的均值,reduce_mean
            loss = -tf.reduce_mean(loglik * advantages)

            trainable_vars = tf.trainable_variables()  # 獲得圖內需要訓練的 variables
            # 設置batch更新trainable_variables的placeholder
            batchGrad = []
            for var in tf.trainable_variables():
                var_name = "batch_" + var.name.split(":")[0]
                batchGrad.append(tf.placeholder(tf.float32, name=var_name))

            newGrads = tf.gradients(loss, trainable_vars)  # 獲得loss對於圖內需要訓練的 variables 的梯度
            adam = tf.train.AdamOptimizer(learning_rate=self.learning_rate)  # 優化器
            # 定義對參數 tvars 進行梯度更新的操作, 使用adam優化器對參數進行更新
            updateGrads = adam.apply_gradients(zip(batchGrad, trainable_vars))
            # 直接優化
            self.opt = adam.minimize(loss)

            self.op_init = tf.global_variables_initializer()
            self.observations = observations
            self.actions = actions
            self.advantages = advantages
            self.batchGrad = batchGrad
            self.probability = probability
            self.trainable_vars = trainable_vars
            self.newGrads = newGrads
            self.updateGrads = updateGrads

    def grad_buffer(self):
        return self.sess.run(self.trainable_vars)

    def action_prob(self, input_x):
        act_prob = self.sess.run(self.probability, feed_dict={self.observations: input_x})
        return act_prob

    def new_grads(self, observations, actions, discounted_rewards):
        # 返回神經網絡各參數對應loss的梯度
        n_grads = self.sess.run(self.newGrads, feed_dict={self.observations: observations, \
                                                          self.actions: actions, self.advantages: discounted_rewards})
        return n_grads

    def update_grads(self, input_gradBuffer):
        self.sess.run(self.updateGrads, feed_dict=dict(zip(self.batchGrad, input_gradBuffer)))

    def update_opt(self, observations, actions, discounted_rewards):
        self.sess.run(self.opt, feed_dict={self.observations: observations, \
                                                          self.actions: actions, self.advantages: discounted_rewards})


p_net = Policy_Net(config)
observation = np.array([[0.1, 0.1, 0.1, 0.1]])

a = time.time()
for _ in range(10000*100):
    p_net.action_prob(observation)
b = time.time()
print(b-a)
View Code

A操作在GPU上時,A操作運行時間:

264.3311233520508秒

264.20179986953735

CPU利用率:

GPU利用率: 

 

 

 

 

 

A操作在CPU上時,A操作運行時間:

90.15146541595459秒

90.39280557632446秒

CPU利用率:

 

 

GPU利用率: 0%

 

可以看到A操作在CPU上運行的效率為在GPU上運行的  2.93倍,在CPU上運行A操作效率更好。

 

 

 ********************************************

 

 

那么A操作和B操作一起操作,A操作在CPU和GPU上效率又會如何呢:(這里我們對B操作進行模擬,也就是在每次迭代更新時增加在CPU上的運算量即可)

代碼:

import tensorflow as tf
import numpy as np
import time

flags = tf.app.flags
flags.DEFINE_integer('D', 4, '輸入層維度')
flags.DEFINE_integer('H', 50, '隱藏層維度')
flags.DEFINE_float('learning_rate', 1e-4, '學習率')
config = flags.FLAGS


"""
這個module是對神經網絡的定義
"""
import tensorflow as tf


class Policy_Net(object):
    def __init__(self, config):
        self.learning_rate = config.learning_rate  # 1e-2
        self.D = config.D  # 4 輸入向量維度
        self.H = config.H  # 50 隱藏層維度
        # 設置新的graph
        self.graph = tf.Graph()
        # 設置顯存的上限大小
        gpu_options = tf.GPUOptions(allow_growth=True)  # 按照計算需求動態申請內存
        # 設置新的session
        self.sess = tf.Session(graph=self.graph, config=tf.ConfigProto(gpu_options=gpu_options))

        self.net_build()  # 構建網絡
        self.sess.run(self.op_init)  # 全局變量初始化

    def net_build(self):  # 構建網絡
        with self.graph.as_default():
            observations = tf.placeholder(tf.float32, [None, self.D], name="input_states")  # 傳入的觀測到的狀態tensor
            actions = tf.placeholder(tf.float32, [None, 1], name="input_actions")
            advantages = tf.placeholder(tf.float32, [None, 1], name="reward_signal")

            # 根據策略網絡計算出選動作action_0的概率probability, action_1的概率為1-probability
            w1 = tf.get_variable("w1", shape=[self.D, self.H],
                                 initializer=tf.contrib.layers.xavier_initializer())
            b1 = tf.get_variable("b1", shape=[self.H], initializer=tf.constant_initializer(0.0))
            layer1 = tf.nn.relu(tf.add(tf.matmul(observations, w1), b1))
            w2 = tf.get_variable("w2", shape=[self.H, 1],
                                 initializer=tf.contrib.layers.xavier_initializer())
            b2 = tf.get_variable("b2", shape=[1], initializer=tf.constant_initializer(0.0))
            score = tf.add(tf.matmul(layer1, w2), b2)
            """ probability為選擇action=2 的概率, action=1的選擇概率為 1-probability """
            probability = tf.nn.sigmoid(score)  # 動作0的actions=0, 動作1的actions=1

            # advantages 為選擇動作actions后所得到的累計折扣獎勵
            loglik = tf.log(actions * (actions - probability) + (1 - actions) * (actions + probability))
            # loss為一個episode內所有observation,actions,advantages得到的損失的均值,reduce_mean
            loss = -tf.reduce_mean(loglik * advantages)

            trainable_vars = tf.trainable_variables()  # 獲得圖內需要訓練的 variables
            # 設置batch更新trainable_variables的placeholder
            batchGrad = []
            for var in tf.trainable_variables():
                var_name = "batch_" + var.name.split(":")[0]
                batchGrad.append(tf.placeholder(tf.float32, name=var_name))

            newGrads = tf.gradients(loss, trainable_vars)  # 獲得loss對於圖內需要訓練的 variables 的梯度
            adam = tf.train.AdamOptimizer(learning_rate=self.learning_rate)  # 優化器
            # 定義對參數 tvars 進行梯度更新的操作, 使用adam優化器對參數進行更新
            updateGrads = adam.apply_gradients(zip(batchGrad, trainable_vars))
            # 直接優化
            self.opt = adam.minimize(loss)

            self.op_init = tf.global_variables_initializer()
            self.observations = observations
            self.actions = actions
            self.advantages = advantages
            self.batchGrad = batchGrad
            self.probability = probability
            self.trainable_vars = trainable_vars
            self.newGrads = newGrads
            self.updateGrads = updateGrads

    def grad_buffer(self):
        return self.sess.run(self.trainable_vars)

    def action_prob(self, input_x):
        act_prob = self.sess.run(self.probability, feed_dict={self.observations: input_x})
        return act_prob

    def new_grads(self, observations, actions, discounted_rewards):
        # 返回神經網絡各參數對應loss的梯度
        n_grads = self.sess.run(self.newGrads, feed_dict={self.observations: observations, \
                                                          self.actions: actions, self.advantages: discounted_rewards})
        return n_grads

    def update_grads(self, input_gradBuffer):
        self.sess.run(self.updateGrads, feed_dict=dict(zip(self.batchGrad, input_gradBuffer)))

    def update_opt(self, observations, actions, discounted_rewards):
        self.sess.run(self.opt, feed_dict={self.observations: observations, \
                                                          self.actions: actions, self.advantages: discounted_rewards})


p_net = Policy_Net(config)

a = time.time()
for _ in range(10000*100):
    observation = np.random.normal(size=[1, 4])
    observation= observation*observation+np.matmul(observation, observation.transpose())
    p_net.action_prob(observation)
b = time.time()
print(b-a)
View Code

 

A操作在GPU上時,A、B操作的運算情況:

用時:

307.19570112228394

312.24832940101624

CPU上利用率:

 

 GPU上利用率:

 

 

 

 

 

 

A操作在CPU上時,A、B操作的運算情況:

用時:

104.759694814682

106.24265789985657

CPU上利用率:

 

 

 

GPU上利用率:0%

 

=========================================

 

假如我們不考慮A、B操作,只考慮C操作,那么是使用CPU還是GPU呢?

代碼:

import tensorflow as tf
import numpy as np
import time

flags = tf.app.flags
flags.DEFINE_integer('D', 4, '輸入層維度')
flags.DEFINE_integer('H', 50, '隱藏層維度')
flags.DEFINE_float('learning_rate', 1e-4, '學習率')
config = flags.FLAGS


"""
這個module是對神經網絡的定義
"""
import tensorflow as tf


class Policy_Net(object):
    def __init__(self, config):
        self.learning_rate = config.learning_rate  # 1e-2
        self.D = config.D  # 4 輸入向量維度
        self.H = config.H  # 50 隱藏層維度
        # 設置新的graph
        self.graph = tf.Graph()
        # 設置顯存的上限大小
        gpu_options = tf.GPUOptions(allow_growth=True)  # 按照計算需求動態申請內存
        # 設置新的session
        self.sess = tf.Session(graph=self.graph, config=tf.ConfigProto(gpu_options=gpu_options))

        self.net_build()  # 構建網絡
        self.sess.run(self.op_init)  # 全局變量初始化

    def net_build(self):  # 構建網絡
        with self.graph.as_default():
            observations = tf.placeholder(tf.float32, [None, self.D], name="input_states")  # 傳入的觀測到的狀態tensor
            actions = tf.placeholder(tf.float32, [None, 1], name="input_actions")
            advantages = tf.placeholder(tf.float32, [None, 1], name="reward_signal")

            # 根據策略網絡計算出選動作action_0的概率probability, action_1的概率為1-probability
            w1 = tf.get_variable("w1", shape=[self.D, self.H],
                                 initializer=tf.contrib.layers.xavier_initializer())
            b1 = tf.get_variable("b1", shape=[self.H], initializer=tf.constant_initializer(0.0))
            layer1 = tf.nn.relu(tf.add(tf.matmul(observations, w1), b1))
            w2 = tf.get_variable("w2", shape=[self.H, 1],
                                 initializer=tf.contrib.layers.xavier_initializer())
            b2 = tf.get_variable("b2", shape=[1], initializer=tf.constant_initializer(0.0))
            score = tf.add(tf.matmul(layer1, w2), b2)
            """ probability為選擇action=2 的概率, action=1的選擇概率為 1-probability """
            probability = tf.nn.sigmoid(score)  # 動作0的actions=0, 動作1的actions=1

            # advantages 為選擇動作actions后所得到的累計折扣獎勵
            loglik = tf.log(actions * (actions - probability) + (1 - actions) * (actions + probability))
            # loss為一個episode內所有observation,actions,advantages得到的損失的均值,reduce_mean
            loss = -tf.reduce_mean(loglik * advantages)

            trainable_vars = tf.trainable_variables()  # 獲得圖內需要訓練的 variables
            # 設置batch更新trainable_variables的placeholder
            batchGrad = []
            for var in tf.trainable_variables():
                var_name = "batch_" + var.name.split(":")[0]
                batchGrad.append(tf.placeholder(tf.float32, name=var_name))

            newGrads = tf.gradients(loss, trainable_vars)  # 獲得loss對於圖內需要訓練的 variables 的梯度
            adam = tf.train.AdamOptimizer(learning_rate=self.learning_rate)  # 優化器
            # 定義對參數 tvars 進行梯度更新的操作, 使用adam優化器對參數進行更新
            updateGrads = adam.apply_gradients(zip(batchGrad, trainable_vars))
            # 直接優化
            self.opt = adam.minimize(loss)

            self.op_init = tf.global_variables_initializer()
            self.observations = observations
            self.actions = actions
            self.advantages = advantages
            self.batchGrad = batchGrad
            self.probability = probability
            self.trainable_vars = trainable_vars
            self.newGrads = newGrads
            self.updateGrads = updateGrads

    def grad_buffer(self):
        return self.sess.run(self.trainable_vars)

    def action_prob(self, input_x):
        act_prob = self.sess.run(self.probability, feed_dict={self.observations: input_x})
        return act_prob

    def new_grads(self, observations, actions, discounted_rewards):
        # 返回神經網絡各參數對應loss的梯度
        n_grads = self.sess.run(self.newGrads, feed_dict={self.observations: observations, \
                                                          self.actions: actions, self.advantages: discounted_rewards})
        return n_grads

    def update_grads(self, input_gradBuffer):
        self.sess.run(self.updateGrads, feed_dict=dict(zip(self.batchGrad, input_gradBuffer)))

    def update_opt(self, observations, actions, discounted_rewards):
        self.sess.run(self.opt, feed_dict={self.observations: observations, \
                                                          self.actions: actions, self.advantages: discounted_rewards})


p_net = Policy_Net(config)
exp_observation = np.random.normal(size=(32, 4))
exp_action = np.random.randint(2, size=[32, 1])*1.0
exp_reward = np.random.randint(99, size=[32, 1])*1.0
a = time.time()
for _ in range(10000*100):
    """
    observation = np.random.normal(size=[1, 4])
    observation= observation*observation+np.matmul(observation, observation.transpose())
    p_net.action_prob(observation)
    """
    p_net.update_opt(exp_observation, exp_action, exp_reward)
b = time.time()
print(b-a)
View Code

 

 

C操作在CPU上:   

用時:

221.16503357887268

218.87172484397888

CPU利用率:

 

 GPU利用率:  0%

 

 

C操作只在GPU上:

用時:

523.3830518722534

CPU利用率:

 

 

GPU利用率:

 

 

 

 

可以看到如果網絡結構過於簡單並且輸入數據過於小時,C操作在CPU上運行的效率高於GPU上。

 

 

 

 

 

如果我們加大輸入數據的大小呢,每次訓練使用較大數據量呢?

import tensorflow as tf
import numpy as np
import time

flags = tf.app.flags
flags.DEFINE_integer('D', 4, '輸入層維度')
flags.DEFINE_integer('H', 50, '隱藏層維度')
flags.DEFINE_float('learning_rate', 1e-4, '學習率')
config = flags.FLAGS


"""
這個module是對神經網絡的定義
"""
import tensorflow as tf


class Policy_Net(object):
    def __init__(self, config):
        self.learning_rate = config.learning_rate  # 1e-2
        self.D = config.D  # 4 輸入向量維度
        self.H = config.H  # 50 隱藏層維度
        # 設置新的graph
        self.graph = tf.Graph()
        # 設置顯存的上限大小
        gpu_options = tf.GPUOptions(allow_growth=True)  # 按照計算需求動態申請內存
        # 設置新的session
        self.sess = tf.Session(graph=self.graph, config=tf.ConfigProto(gpu_options=gpu_options))

        self.net_build()  # 構建網絡
        self.sess.run(self.op_init)  # 全局變量初始化

    def net_build(self):  # 構建網絡
        with self.graph.as_default():
            observations = tf.placeholder(tf.float32, [None, self.D], name="input_states")  # 傳入的觀測到的狀態tensor
            actions = tf.placeholder(tf.float32, [None, 1], name="input_actions")
            advantages = tf.placeholder(tf.float32, [None, 1], name="reward_signal")

            # 根據策略網絡計算出選動作action_0的概率probability, action_1的概率為1-probability
            w1 = tf.get_variable("w1", shape=[self.D, self.H],
                                 initializer=tf.contrib.layers.xavier_initializer())
            b1 = tf.get_variable("b1", shape=[self.H], initializer=tf.constant_initializer(0.0))
            layer1 = tf.nn.relu(tf.add(tf.matmul(observations, w1), b1))
            w2 = tf.get_variable("w2", shape=[self.H, 1],
                                 initializer=tf.contrib.layers.xavier_initializer())
            b2 = tf.get_variable("b2", shape=[1], initializer=tf.constant_initializer(0.0))
            score = tf.add(tf.matmul(layer1, w2), b2)
            """ probability為選擇action=2 的概率, action=1的選擇概率為 1-probability """
            probability = tf.nn.sigmoid(score)  # 動作0的actions=0, 動作1的actions=1

            # advantages 為選擇動作actions后所得到的累計折扣獎勵
            loglik = tf.log(actions * (actions - probability) + (1 - actions) * (actions + probability))
            # loss為一個episode內所有observation,actions,advantages得到的損失的均值,reduce_mean
            loss = -tf.reduce_mean(loglik * advantages)

            trainable_vars = tf.trainable_variables()  # 獲得圖內需要訓練的 variables
            # 設置batch更新trainable_variables的placeholder
            batchGrad = []
            for var in tf.trainable_variables():
                var_name = "batch_" + var.name.split(":")[0]
                batchGrad.append(tf.placeholder(tf.float32, name=var_name))

            newGrads = tf.gradients(loss, trainable_vars)  # 獲得loss對於圖內需要訓練的 variables 的梯度
            adam = tf.train.AdamOptimizer(learning_rate=self.learning_rate)  # 優化器
            # 定義對參數 tvars 進行梯度更新的操作, 使用adam優化器對參數進行更新
            updateGrads = adam.apply_gradients(zip(batchGrad, trainable_vars))
            # 直接優化
            self.opt = adam.minimize(loss)

            self.op_init = tf.global_variables_initializer()
            self.observations = observations
            self.actions = actions
            self.advantages = advantages
            self.batchGrad = batchGrad
            self.probability = probability
            self.trainable_vars = trainable_vars
            self.newGrads = newGrads
            self.updateGrads = updateGrads

    def grad_buffer(self):
        return self.sess.run(self.trainable_vars)

    def action_prob(self, input_x):
        act_prob = self.sess.run(self.probability, feed_dict={self.observations: input_x})
        return act_prob

    def new_grads(self, observations, actions, discounted_rewards):
        # 返回神經網絡各參數對應loss的梯度
        n_grads = self.sess.run(self.newGrads, feed_dict={self.observations: observations, \
                                                          self.actions: actions, self.advantages: discounted_rewards})
        return n_grads

    def update_grads(self, input_gradBuffer):
        self.sess.run(self.updateGrads, feed_dict=dict(zip(self.batchGrad, input_gradBuffer)))

    def update_opt(self, observations, actions, discounted_rewards):
        self.sess.run(self.opt, feed_dict={self.observations: observations, \
                                                          self.actions: actions, self.advantages: discounted_rewards})


p_net = Policy_Net(config)
exp_observation = np.random.normal(size=(8096, 4))
exp_action = np.random.randint(2, size=[8096, 1])*1.0
exp_reward = np.random.randint(99, size=[8096, 1])*1.0
a = time.time()
for _ in range(10000*100):
    """
    observation = np.random.normal(size=[1, 4])
    observation= observation*observation+np.matmul(observation, observation.transpose())
    p_net.action_prob(observation)
    """
    p_net.update_opt(exp_observation, exp_action, exp_reward)
b = time.time()
print(b-a)
View Code

 

C操作只在GPU上:

用時:

669.1641449928284

CPU利用率:

 

 

GPU利用率:

 

 

 

 

 

 

C操作只在CPU上:

用時:

 914.7391726970673

CPU利用率:

 

GPU利用率:0%

 

 

可以看到C操作部分只有當數據量較大(batch_size較大時)或者神經網絡較深(網絡較復雜時),使用GPU操作要優於使用CPU操作的,而在決策學習中(強化學習)這兩點有時是難以滿足的。如果batch數據較小或者網絡較為簡單時我們使用CPU端進行計算會更加高效。

 

===============================================

 

 

最后我們將A、B、C操作聯合在一起,這也是一個完整決策學習的標准過程,我們看看將A、C操作反正CPU上還是GPU上速度更快。這里我們將C操作分為batch數據較大和較小兩種情況來考察:

 

 

------------------------

 

batch數據較小情況:(batch_size 為32)

代碼:

import tensorflow as tf
import numpy as np
import time

flags = tf.app.flags
flags.DEFINE_integer('D', 4, '輸入層維度')
flags.DEFINE_integer('H', 50, '隱藏層維度')
flags.DEFINE_float('learning_rate', 1e-4, '學習率')
config = flags.FLAGS


"""
這個module是對神經網絡的定義
"""
import tensorflow as tf


class Policy_Net(object):
    def __init__(self, config):
        self.learning_rate = config.learning_rate  # 1e-2
        self.D = config.D  # 4 輸入向量維度
        self.H = config.H  # 50 隱藏層維度
        # 設置新的graph
        self.graph = tf.Graph()
        # 設置顯存的上限大小
        gpu_options = tf.GPUOptions(allow_growth=True)  # 按照計算需求動態申請內存
        # 設置新的session
        self.sess = tf.Session(graph=self.graph, config=tf.ConfigProto(gpu_options=gpu_options))

        self.net_build()  # 構建網絡
        self.sess.run(self.op_init)  # 全局變量初始化

    def net_build(self):  # 構建網絡
        with self.graph.as_default():
            observations = tf.placeholder(tf.float32, [None, self.D], name="input_states")  # 傳入的觀測到的狀態tensor
            actions = tf.placeholder(tf.float32, [None, 1], name="input_actions")
            advantages = tf.placeholder(tf.float32, [None, 1], name="reward_signal")

            # 根據策略網絡計算出選動作action_0的概率probability, action_1的概率為1-probability
            w1 = tf.get_variable("w1", shape=[self.D, self.H],
                                 initializer=tf.contrib.layers.xavier_initializer())
            b1 = tf.get_variable("b1", shape=[self.H], initializer=tf.constant_initializer(0.0))
            layer1 = tf.nn.relu(tf.add(tf.matmul(observations, w1), b1))
            w2 = tf.get_variable("w2", shape=[self.H, 1],
                                 initializer=tf.contrib.layers.xavier_initializer())
            b2 = tf.get_variable("b2", shape=[1], initializer=tf.constant_initializer(0.0))
            score = tf.add(tf.matmul(layer1, w2), b2)
            """ probability為選擇action=2 的概率, action=1的選擇概率為 1-probability """
            probability = tf.nn.sigmoid(score)  # 動作0的actions=0, 動作1的actions=1

            # advantages 為選擇動作actions后所得到的累計折扣獎勵
            loglik = tf.log(actions * (actions - probability) + (1 - actions) * (actions + probability))
            # loss為一個episode內所有observation,actions,advantages得到的損失的均值,reduce_mean
            loss = -tf.reduce_mean(loglik * advantages)

            trainable_vars = tf.trainable_variables()  # 獲得圖內需要訓練的 variables
            # 設置batch更新trainable_variables的placeholder
            batchGrad = []
            for var in tf.trainable_variables():
                var_name = "batch_" + var.name.split(":")[0]
                batchGrad.append(tf.placeholder(tf.float32, name=var_name))

            newGrads = tf.gradients(loss, trainable_vars)  # 獲得loss對於圖內需要訓練的 variables 的梯度
            adam = tf.train.AdamOptimizer(learning_rate=self.learning_rate)  # 優化器
            # 定義對參數 tvars 進行梯度更新的操作, 使用adam優化器對參數進行更新
            updateGrads = adam.apply_gradients(zip(batchGrad, trainable_vars))
            # 直接優化
            self.opt = adam.minimize(loss)

            self.op_init = tf.global_variables_initializer()
            self.observations = observations
            self.actions = actions
            self.advantages = advantages
            self.batchGrad = batchGrad
            self.probability = probability
            self.trainable_vars = trainable_vars
            self.newGrads = newGrads
            self.updateGrads = updateGrads

    def grad_buffer(self):
        return self.sess.run(self.trainable_vars)

    def action_prob(self, input_x):
        act_prob = self.sess.run(self.probability, feed_dict={self.observations: input_x})
        return act_prob

    def new_grads(self, observations, actions, discounted_rewards):
        # 返回神經網絡各參數對應loss的梯度
        n_grads = self.sess.run(self.newGrads, feed_dict={self.observations: observations, \
                                                          self.actions: actions, self.advantages: discounted_rewards})
        return n_grads

    def update_grads(self, input_gradBuffer):
        self.sess.run(self.updateGrads, feed_dict=dict(zip(self.batchGrad, input_gradBuffer)))

    def update_opt(self, observations, actions, discounted_rewards):
        self.sess.run(self.opt, feed_dict={self.observations: observations, \
                                                          self.actions: actions, self.advantages: discounted_rewards})

batch_size = 32
p_net = Policy_Net(config)
exp_observation = np.random.normal(size=(batch_size, 4))
exp_action = np.random.randint(2, size=[batch_size, 1])*1.0
exp_reward = np.random.randint(99, size=[batch_size, 1])*1.0
a = time.time()
for _ in range(10000*100):

    observation = np.random.normal(size=[1, 4])
    observation= observation*observation+np.matmul(observation, observation.transpose())
    p_net.action_prob(observation)

    p_net.update_opt(exp_observation, exp_action, exp_reward)
b = time.time()
print(b-a)
View Code

 

A、C操作在CPU上時:

用時:

403.56523847579956

CPU利用率:

 

GPU利用率: 0%

 

 

 

 

A、C操作在GPU上時:

用時:

915.9552888870239

CPU利用率:

 

GPU利用率:

 

 

------------------------

 

 

batch數據較大情況:(batch_size 為8096)

代碼:

import tensorflow as tf
import numpy as np
import time

flags = tf.app.flags
flags.DEFINE_integer('D', 4, '輸入層維度')
flags.DEFINE_integer('H', 50, '隱藏層維度')
flags.DEFINE_float('learning_rate', 1e-4, '學習率')
config = flags.FLAGS


"""
這個module是對神經網絡的定義
"""
import tensorflow as tf


class Policy_Net(object):
    def __init__(self, config):
        self.learning_rate = config.learning_rate  # 1e-2
        self.D = config.D  # 4 輸入向量維度
        self.H = config.H  # 50 隱藏層維度
        # 設置新的graph
        self.graph = tf.Graph()
        # 設置顯存的上限大小
        gpu_options = tf.GPUOptions(allow_growth=True)  # 按照計算需求動態申請內存
        # 設置新的session
        self.sess = tf.Session(graph=self.graph, config=tf.ConfigProto(gpu_options=gpu_options))

        self.net_build()  # 構建網絡
        self.sess.run(self.op_init)  # 全局變量初始化

    def net_build(self):  # 構建網絡
        with self.graph.as_default():
            observations = tf.placeholder(tf.float32, [None, self.D], name="input_states")  # 傳入的觀測到的狀態tensor
            actions = tf.placeholder(tf.float32, [None, 1], name="input_actions")
            advantages = tf.placeholder(tf.float32, [None, 1], name="reward_signal")

            # 根據策略網絡計算出選動作action_0的概率probability, action_1的概率為1-probability
            w1 = tf.get_variable("w1", shape=[self.D, self.H],
                                 initializer=tf.contrib.layers.xavier_initializer())
            b1 = tf.get_variable("b1", shape=[self.H], initializer=tf.constant_initializer(0.0))
            layer1 = tf.nn.relu(tf.add(tf.matmul(observations, w1), b1))
            w2 = tf.get_variable("w2", shape=[self.H, 1],
                                 initializer=tf.contrib.layers.xavier_initializer())
            b2 = tf.get_variable("b2", shape=[1], initializer=tf.constant_initializer(0.0))
            score = tf.add(tf.matmul(layer1, w2), b2)
            """ probability為選擇action=2 的概率, action=1的選擇概率為 1-probability """
            probability = tf.nn.sigmoid(score)  # 動作0的actions=0, 動作1的actions=1

            # advantages 為選擇動作actions后所得到的累計折扣獎勵
            loglik = tf.log(actions * (actions - probability) + (1 - actions) * (actions + probability))
            # loss為一個episode內所有observation,actions,advantages得到的損失的均值,reduce_mean
            loss = -tf.reduce_mean(loglik * advantages)

            trainable_vars = tf.trainable_variables()  # 獲得圖內需要訓練的 variables
            # 設置batch更新trainable_variables的placeholder
            batchGrad = []
            for var in tf.trainable_variables():
                var_name = "batch_" + var.name.split(":")[0]
                batchGrad.append(tf.placeholder(tf.float32, name=var_name))

            newGrads = tf.gradients(loss, trainable_vars)  # 獲得loss對於圖內需要訓練的 variables 的梯度
            adam = tf.train.AdamOptimizer(learning_rate=self.learning_rate)  # 優化器
            # 定義對參數 tvars 進行梯度更新的操作, 使用adam優化器對參數進行更新
            updateGrads = adam.apply_gradients(zip(batchGrad, trainable_vars))
            # 直接優化
            self.opt = adam.minimize(loss)

            self.op_init = tf.global_variables_initializer()
            self.observations = observations
            self.actions = actions
            self.advantages = advantages
            self.batchGrad = batchGrad
            self.probability = probability
            self.trainable_vars = trainable_vars
            self.newGrads = newGrads
            self.updateGrads = updateGrads

    def grad_buffer(self):
        return self.sess.run(self.trainable_vars)

    def action_prob(self, input_x):
        act_prob = self.sess.run(self.probability, feed_dict={self.observations: input_x})
        return act_prob

    def new_grads(self, observations, actions, discounted_rewards):
        # 返回神經網絡各參數對應loss的梯度
        n_grads = self.sess.run(self.newGrads, feed_dict={self.observations: observations, \
                                                          self.actions: actions, self.advantages: discounted_rewards})
        return n_grads

    def update_grads(self, input_gradBuffer):
        self.sess.run(self.updateGrads, feed_dict=dict(zip(self.batchGrad, input_gradBuffer)))

    def update_opt(self, observations, actions, discounted_rewards):
        self.sess.run(self.opt, feed_dict={self.observations: observations, \
                                                          self.actions: actions, self.advantages: discounted_rewards})

batch_size = 8096
p_net = Policy_Net(config)
exp_observation = np.random.normal(size=(batch_size, 4))
exp_action = np.random.randint(2, size=[batch_size, 1])*1.0
exp_reward = np.random.randint(99, size=[batch_size, 1])*1.0
a = time.time()
for _ in range(10000*100):

    observation = np.random.normal(size=[1, 4])
    observation= observation*observation+np.matmul(observation, observation.transpose())
    p_net.action_prob(observation)

    p_net.update_opt(exp_observation, exp_action, exp_reward)
b = time.time()
print(b-a)
View Code

 

A、C操作在CPU上時:

用時:

 1069.901896238327

 

CPU利用率:

 

GPU利用率:  0%

 

 

 

 

 

 

 

A、C操作在GPU上時:

用時:

1042.1329367160797

 

CPU利用率:

 

 

GPU利用率:

 

 

 

===============================================

 

總結:  

CPU上運行TensorFlow,由於CPU並行能力弱於GPU,如果數據的計算量較大並且能很好的進行並行化,那么GPU的運算效果優於CPU。但是如果計算量較小並且不能很好的並行化(或者只能串行話),那么由於CPU的串行運算能力優於GPU,此時使用CPU運算效果將會由於GPU。

根據上面對決策學習的分析,如果再更新網絡時使用較少數據或者網絡過於簡單,那么在CPU上進行運算將會優於在GPU上進行運算,而在決策學習中該種情況更為常見,也就是說在決策學習(強化學習)中使用CPU計算往往會優於使用GPU進行運算。

從上面的分析來看如何在強化學習中更好的提高運算速度並且不影響模型效果是一個有實際價值的事情。

 

===========================================

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM