Tensorflow (GPU) против Numpy

поэтому у меня есть две реализации линейной регрессии с использованием градиентного спуска. Один в Tensorflow, один в Numpy. Я обнаружил, что в Numpy он примерно в 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)

И тупой:

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 (960). Код Tensorflow работает примерно 20 секунд, а код Numpy — около 7 секунд в том же наборе данных. Набор данных составляет почти 1 миллион строк.

Я ожидал, что Tensorflow легко победит Numpy, но это не так. Конечно, я новичок в использовании Tensorflow, и реализация Numpy не использует класс, но все же с Numpy в 3 раза лучше?!

Надеясь на некоторые мысли/идеи о том, что я делаю неправильно здесь.


person Calvin_xc1    schedule 17.09.2017    source источник
comment
Обратите внимание, что numpy и TensorFlow имеют отдельные системы памяти, когда вы выполняете sess.run([optimizer, self.error()], feed_dict={, он копирует данные из numpy в пространство графического процессора TensorFlow на каждом шаге.   -  person Yaroslav Bulatov    schedule 18.09.2017


Ответы (1)


Не рассматривая подробно ваш код (не так много опыта работы с TF):

Это сравнение некорректно!

  • Комментарий Ярослава, конечно, верен: GPU-вычисления имеют некоторые накладные расходы (по крайней мере, подготовка данных; не уверен, какая здесь компиляция тактируется)
  • You are comparing pure GD to Adadelta in full-batch mode it seems:
    • Adadelta of course implicates some overhead (there are more operations than calculating the gradient and multiplying the current iterate) as it's one of the common variance-reduction methods which come with a price!
      • The idea is: invest some additional operations to:
        • remove the number of iterations needed given some learning-rate
        • (это даже намного сложнее: для большинства людей -> добиться хорошей конвергенции с использованием скорости обучения по умолчанию)
  • It seems you are just running 100 epochs each and clocking this
    • That's not meaningful!
      • It's very much possible that the objective is very different:
        • if iteration size is not enough
        • или начальная скорость обучения выбрана плохо
      • или то же самое, но несуществующая ранняя остановка гарантирует, что возможный лучший алгоритм с доказанной сходимостью (согласно некоторому критерию) тратит некоторое дополнительное время на выполнение всех итераций, пока не будет достигнуто 100!
  • (Ададельта, вероятно, была разработана для настройки SGD, а не GD)

Сравнивать такие разные алгоритмы очень сложно, особенно при использовании только одной задачи/набора данных.

Даже если вы введете раннюю остановку, вы увидите недетерминированную производительность на основе случайного начального числа, которую трудно интерпретировать.

В основном вы измеряете время итерации, но это не очень хорошая мера. Сравните методы первого порядка (градиенты -> SGD, GD, ...) с методами второго порядка (Гессиан -> Ньютон). ). последний очень медленно итерируется, но обычно имеет поведение квадратичной сходимости, что приводит к тому, что требуется гораздо меньше итераций! В NN-приложениях этот пример больше: LBFGS vs. SGD/... (хотя я не знаю, доступен ли LBFGS в TF; torch поддерживает это). Известно, что LBFGS обеспечивает локальную квадратичную сходимость, которую опять же трудно интерпретировать в реальных задачах (особенно потому, что эта ограниченная память аппроксимация обратного гессиана является параметром LBFGS). Это сравнение также можно провести в линейном программировании, где симплекс-метод имеет быстрые итерации, в то время как методы внутренних точек (в основном основанные на Ньютоне; но при рассмотрении оптимизации с ограничениями здесь необходимы некоторые дополнительные идеи) намного медленнее на итерацию (несмотря на быстрее достичь сходимости во многих случаях).

Что я здесь проигнорировал: почти все теоретические результаты, касающиеся конвергенции и т.д. ограничиваются выпуклыми и гладкими функциями. NN обычно невыпуклы, а это означает, что задача оценки этих показателей производительности еще сложнее. Но ваша проблема здесь, конечно, выпуклая.

Я также должен признать, что мой ответ лишь поверхностно затрагивает эту сложную проблему, даже если неограниченная гладкая выпуклая оптимизация является одной из самых простых задач в численной оптимизации (по сравнению с ограниченной, негладкой невыпуклой оптимизацией).

Для общего введения в числовую оптимизацию, в которой также много говорится о методах первого и второго порядка (и существует много промежуточных методов), я рекомендую Числовая оптимизация от Nocedal and Wright, которую можно найти в Интернете.

person sascha    schedule 18.09.2017