python - Tensorflow (GPU) 与 Numpy

标签 python numpy tensorflow

所以我有两个使用梯度下降的线性回归实现。一个在 Tensorflow 中,一个在 Numpy 中。我发现 Numpy 中的那个比 Tensorflow 中的快大约 3 倍。这是我的代码-

tensorflow :

class network_cluster(object):
    def __init__(self, data_frame, feature_cols, label_cols):
        self.init_data(data_frame, feature_cols, label_cols)
        self.init_tensors()

    def init_data(self, data_frame, feature_cols, label_cols):
        self.data_frame = data_frame
        self.feature_cols = feature_cols
        self.label_cols = label_cols

    def init_tensors(self):
        self.features = tf.placeholder(tf.float32)
        self.labels = tf.placeholder(tf.float32)

        self.weights = tf.Variable(tf.random_normal((len(self.feature_cols), len(self.label_cols))))
        self.const = tf.Variable(tf.random_normal((len(self.label_cols),)))

    def linear_combiner(self):
        return tf.add(tf.matmul(self.features, self.weights), self.const)

    def predict(self):
        return self.linear_combiner()

    def error(self):
        return tf.reduce_mean(tf.pow(self.labels - self.predict(), 2), axis = 0)

    def learn_model(self, epocs = 100):
        optimizer = tf.train.AdadeltaOptimizer(1).minimize(self.error())

        error_rcd = []
        with tf.Session() as sess:
            sess.run(tf.global_variables_initializer())
            for epoc in range(epocs):
                _, error = sess.run([optimizer, self.error()], feed_dict={
                    self.features: self.data_frame[self.feature_cols],
                    self.labels: self.data_frame[self.label_cols]
                })
                error_rcd.append(error[0])

        return error_rcd

    def get_coefs(self):
        with tf.Session() as sess:
            sess.run(tf.global_variables_initializer())

            coefs = sess.run([self.weights, self.const])

        return coefs

test_cluster = network_cluster(dataset, ['ship_jumps', 'npc_kills', 'ship_kills', 'pod_kills'], ['hour_of_week'])
%timeit test_cluster.learn_model(epocs = 100)

和 NumPy 的:

def grad_descent(dataset, features, predictor, max_iters = 10000):

    def initialize_model(dataset, features, predictor):
        constant_array = np.ones(shape = (len(dataset), 1))
        features_array = dataset.loc[:, features].values
        features_array = np.append(constant_array, features_array, axis = 1)
        predict_array = dataset.loc[:, predictor].values
        betas = np.zeros(shape = (len(features) + 1, len(predictor)))
        return (features_array, predict_array, betas)

    def calc_gradient(features_array, predict_array, betas):
        prediction = np.dot(features_array, betas)
        predict_error = predict_array - prediction
        gradient = -2 * np.dot(features_array.transpose(), predict_error)
        gradient_two = 2 * np.expand_dims(np.sum(features_array ** 2, axis = 0), axis = 1)
        return (gradient, gradient_two)

    def update_betas(gradient, gradient_two, betas):
        new_betas = betas - ((gradient / gradient_two) / len(betas))
        return new_betas

    def model_error(features_array, predict_array, betas):
        prediction = np.dot(features_array, betas)
        predict_error = predict_array - prediction
        model_error = np.sqrt(np.mean(predict_error ** 2))
        return model_error

    features_array, predict_array, betas = initialize_model(dataset, features, predictor)
    prior_error = np.inf
    for iter_count in range(max_iters):
        gradient, gradient_two = calc_gradient(features_array, predict_array, betas)
        betas = update_betas(gradient, gradient_two, betas)
        curr_error = model_error(features_array, predict_array, betas)
        if curr_error == prior_error:
            break
        prior_error = curr_error
    return (betas, iter_count, curr_error)

%timeit grad_descent(dataset, ['ship_jumps', 'npc_kills', 'ship_kills', 'pod_kills'], ['hour_of_week'], max_iters = 100)

我正在使用 Spyder IDE 进行测试,并且我有一个 Nvidia GPU (960)。在同一数据集上,Tensorflow 代码的计时时间约为 20 秒,而 Numpy 代码的计时时间约为 7 秒。该数据集将近 100 万行。

我原以为 Tensorflow 会在这里轻而易举地击败 Numpy,但事实并非如此。当然,我是 Tensorflow 的新手,Numpy 实现不使用类,但使用 Numpy 还是要好 3 倍?!

希望对我在这里做错的事情有一些想法/想法。

最佳答案

无需详细查看您的代码(使用 TF 的经验不多):

这种比较是有缺陷的!

  • Yaroslav 的评论当然是正确的:GPU 计算有一些开销(至少是数据准备;不确定这里计时的是哪种编译)
  • 您似乎在全批处理模式下比较纯 GD 和 Adadelta:
    • Adadelta 当然会涉及一些开销(除了计算梯度和乘以当前迭代之外还有更多的操作),因为它是一种常见的方差减少方法,需要付出代价!
      • 想法是:投资一些额外的操作来:
        • 删除给定学习率所需的迭代次数
        • (这要复杂得多:对于大多数人来说 -> 使用默认学习率实现良好的收敛)
  • 看起来你只是每次运行 100 个 epochs 并计时
    • 那没有意义!
      • 很可能目标非常不同:
        • 如果迭代大小不够
        • 或者初始学习率选择不当
      • 或相同,但不存在的提前停止确保了可能更好的算法(根据某些标准)证明收敛会浪费一些额外的时间进行所有迭代,直到达到 100!
  • (Adadelta 可能是为 SGD 设置设计的;不是 GD)

很难比较这些不同的算法,尤其是在仅使用一个任务/数据集时。

即使您引入提前停止,您也会观察到基于随机种子的不确定性能,这很难解释。

您基本上是在测量迭代时间,但这不是一个好的测量方法。比较一阶方法(梯度 -> SGD、GD...)和二阶方法(hessian -> 牛顿)。后者的迭代速度非常慢,但通常会获得二次收敛行为,从而减少所需的迭代次数!在 NN 应用程序中,这个例子更多:LBFGS vs. SGD/...(虽然我不知道 LBFGS 在 TF 中是否可用;torch 支持它)。众所周知,LBFGS 可以实现局部二次收敛,这在现实世界的任务中也很难解释(尤其是因为逆黑森矩阵的这种有限内存近似是 LBFGS 的一个参数)。这种比较也可以在线性规划上进行,其中单纯形法具有快速迭代,而内点法(基本上是基于牛顿的;但这里处理约束优化需要一些额外的想法)每次迭代要慢得多(尽管在许多情况下可以更快地实现收敛)。

我在这里忽略的是:几乎所有关于收敛和协同的理论结果。仅限于凸函数和平滑函数。 NN 通常是非凸的,这意味着评估这些性能指标的任务更加困难。但是你这里的问题当然是凸的。

我还必须承认,我的回答只是触及了这个复杂问题的表面,即使无约束平滑凸优化是数值优化中较容易的任务之一(与约束、非平滑非凸优化相比)。

对于数值优化的一般介绍,其中也讨论了很多关于一阶与二阶方法(并且中间有许多方法),我推荐 Numerical Optimization by Nocedal and Wright可以在网上找到。

关于python - Tensorflow (GPU) 与 Numpy,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46269557/

相关文章:

python - Ruamel yaml 转储嵌套列表的格式

python - Numpy:检查 float 组是否包含整数

python - 将 scipy.ndimage.rotate 与 alpha channel 一起使用时图像中的伪影

python - TensorFlow:如何使用 'tf.data' 而不是 'load_csv_without_header'?

python - 请求 : cannot download zip file properly with verify is False

python - 将属性注入(inject) QML 组件

python - Scrapy 没有抓取所有页面

python - Graphlab 和 numpy 问题

python - 如何使用 tf.train.Scaffold 初始化评估的局部变量?

tensorflow - 如何在最新版本的 Tensorflow 中使用 MultiVariateNormal 分布