python - neural - ¿Por qué esta implementación de TensorFlow es mucho menos exitosa que la NN de Matlab?
neural network examples (2)
Intenté entrenar para 50000 iteraciones, llegó al error 0.00012. Tarda unos 180 segundos en Tesla K40.
Parece que para este tipo de problema, el descenso de gradiente de primer orden no es una buena opción (juego de palabras), y necesita Levenberg-Marquardt o l-BFGS. No creo que nadie los haya implementado en TensorFlow todavía.
Edite Use tf.train.AdamOptimizer(0.1)
para este problema. 3.13729e-05
a 3.13729e-05
después de 4000 iteraciones. Además, la GPU con estrategia predeterminada también parece una mala idea para este problema. Hay muchas operaciones pequeñas y la sobrecarga hace que la versión de la GPU se ejecute 3 veces más lenta que la CPU en mi máquina.
Como ejemplo de juguete, intento ajustar una función f(x) = 1/x
partir de 100 puntos de datos sin ruido. La implementación predeterminada de matlab es fenomenalmente exitosa con una diferencia cuadrática media ~ 10 ^ -10, e interpola perfectamente.
Implemento una red neuronal con una capa oculta de 10 neuronas sigmoideas. Soy un principiante en redes neuronales, así que ten cuidado con el código tonto.
import tensorflow as tf
import numpy as np
def weight_variable(shape):
initial = tf.truncated_normal(shape, stddev=0.1)
return tf.Variable(initial)
def bias_variable(shape):
initial = tf.constant(0.1, shape=shape)
return tf.Variable(initial)
#Can''t make tensorflow consume ordinary lists unless they''re parsed to ndarray
def toNd(lst):
lgt = len(lst)
x = np.zeros((1, lgt), dtype=''float32'')
for i in range(0, lgt):
x[0,i] = lst[i]
return x
xBasic = np.linspace(0.2, 0.8, 101)
xTrain = toNd(xBasic)
yTrain = toNd(map(lambda x: 1/x, xBasic))
x = tf.placeholder("float", [1,None])
hiddenDim = 10
b = bias_variable([hiddenDim,1])
W = weight_variable([hiddenDim, 1])
b2 = bias_variable([1])
W2 = weight_variable([1, hiddenDim])
hidden = tf.nn.sigmoid(tf.matmul(W, x) + b)
y = tf.matmul(W2, hidden) + b2
# Minimize the squared errors.
loss = tf.reduce_mean(tf.square(y - yTrain))
optimizer = tf.train.GradientDescentOptimizer(0.5)
train = optimizer.minimize(loss)
# For initializing the variables.
init = tf.initialize_all_variables()
# Launch the graph
sess = tf.Session()
sess.run(init)
for step in xrange(0, 4001):
train.run({x: xTrain}, sess)
if step % 500 == 0:
print loss.eval({x: xTrain}, sess)
La diferencia cuadrática media termina en ~ 2 * 10 ^ -3, por lo que alrededor de 7 órdenes de magnitud son peores que el matlab. Visualizando con
xTest = np.linspace(0.2, 0.8, 1001)
yTest = y.eval({x:toNd(xTest)}, sess)
import matplotlib.pyplot as plt
plt.plot(xTest,yTest.transpose().tolist())
plt.plot(xTest,map(lambda x: 1/x, xTest))
plt.show()
podemos ver que el ajuste es sistemáticamente imperfecto: mientras que el matlab se ve perfecto a simple vista con las diferencias uniformemente <10 ^ -5: He tratado de replicar con TensorFlow el diagrama de la red Matlab:
Incidentalmente, el diagrama parece implicar una función de activación de tanh en lugar de sigmoide. No puedo encontrarlo en ninguna parte de la documentación para estar seguro. Sin embargo, cuando trato de usar una neurona tanh en TensorFlow, el accesorio falla rápidamente con nan
para las variables. No se por que.
Matlab usa el algoritmo de entrenamiento Levenberg-Marquardt. La regularización bayesiana es aún más exitosa con cuadrados medios en 10 ^ -12 (probablemente estamos en el área de los vapores de la aritmética flotante).
¿Por qué la implementación de TensorFlow es mucho peor y qué puedo hacer para mejorarla?
Por cierto, aquí hay una versión ligeramente depurada de lo anterior que limpia algunos de los problemas de forma y rebotes innecesarios entre tf y np. Logra 3e-08 después de 40k pasos, o aproximadamente 1.5e-5 después de 4000:
import tensorflow as tf
import numpy as np
def weight_variable(shape):
initial = tf.truncated_normal(shape, stddev=0.1)
return tf.Variable(initial)
def bias_variable(shape):
initial = tf.constant(0.1, shape=shape)
return tf.Variable(initial)
xTrain = np.linspace(0.2, 0.8, 101).reshape([1, -1])
yTrain = (1/xTrain)
x = tf.placeholder(tf.float32, [1,None])
hiddenDim = 10
b = bias_variable([hiddenDim,1])
W = weight_variable([hiddenDim, 1])
b2 = bias_variable([1])
W2 = weight_variable([1, hiddenDim])
hidden = tf.nn.sigmoid(tf.matmul(W, x) + b)
y = tf.matmul(W2, hidden) + b2
# Minimize the squared errors.
loss = tf.reduce_mean(tf.square(y - yTrain))
step = tf.Variable(0, trainable=False)
rate = tf.train.exponential_decay(0.15, step, 1, 0.9999)
optimizer = tf.train.AdamOptimizer(rate)
train = optimizer.minimize(loss, global_step=step)
init = tf.initialize_all_variables()
# Launch the graph
sess = tf.Session()
sess.run(init)
for step in xrange(0, 40001):
train.run({x: xTrain}, sess)
if step % 500 == 0:
print loss.eval({x: xTrain}, sess)
Dicho todo esto, probablemente no sea demasiado sorprendente que LMA lo esté haciendo mejor que un optimizador de estilo DNN más general para ajustar una curva 2D. Adam y el resto se enfocan en problemas de alta dimensionalidad, y LMA comienza a ser glacialmente lento para redes muy grandes (ver 12-15).