Tensorflow - проверка градиента numpy не работает

Я пытаюсь оценить градиент функции методом конечных разностей: метод конечных разностей для оценки градиента

TL; DR:

grad f(x) = [f(x+h)-f(x-h)]/(2h) для достаточно малых h.

это также используется на этапе проверки градиента, чтобы проверить обратное распространение в ИИ, как вы, возможно, знаете.

Это моя сеть:

      def defineModel():
  global model
  model = Sequential()
  model.add(keras.Input(shape=input_shape))
  model.add(layers.Conv2D(32, kernel_size=(3, 3), activation="relu"))
  model.add(layers.MaxPooling2D(pool_size=(2, 2)))
  model.add(layers.Conv2D(64, kernel_size=(3, 3), activation="relu"))
  model.add(layers.MaxPooling2D(pool_size=(2, 2)))
  model.add( layers.Flatten())
  model.add(layers.Dropout(0.5))
  model.add(layers.Dense(num_classes, activation="softmax"))
  model.build()
  model.summary()

Эта часть в порядке и не содержит ошибок. Я просто упомянул об этом здесь, чтобы вы имели представление о моей модели. Я работаю над MNIST, так что все довольно просто. С 1 эпохой и несколькими строками кода TF я достиг точности +98%, что неплохо для импровизированной модели.

Поскольку я занимаюсь состязательным обучением, мне нужен градиент моей потери относительно входных данных:

кстати, я использовал идею тайлинга:

если вы покрываете входное изображение квадратными плитками размера (плитка * плитка) без перекрытия, вы можете предположить, что градиент изображения внутри плиток в значительной степени постоянен, так что это разумное приближение. Но что касается отладки, в моем коде tile=1 поэтому мы вычисляем градиент по пикселям.

и в этом проблема, но я не могу понять где! Я контролировал потерю loss(x+h) и loss(x-h) и loss(x)находятся на близком расстоянии, так что я знаю, что это нормально. также моя автоматическая обратная связь TF работает нормально, я ее тестировал. проблема должна быть в способе вычисления градиента вручную.

      tile=1
h=1e-4 #also tried 1e-5, 1e-6 but did not work

#A dummy function to wait
def w():
  print()
  ww=input('wait')
  print()

#This function works fine.
def rel_error(x, y):
  """ returns relative error """
  return np.max(np.abs(x - y) / (np.maximum(1e-8, np.abs(x) + np.abs(y))))

#the problem is here. given an index suah is idx, I'm going to manipulate
#x_test[idx] and compute loss gradient with respect to this input
def estimateGrad(idx):
  y=model(np.expand_dims(x_test[idx],axis=0))
  y=np.expand_dims(y_train[idx],axis=0)
  x=np.squeeze(x_test[idx])

  

  cce = tf.keras.losses.CategoricalCrossentropy()
  grad=np.zeros((28,28))
  num=int(28/tile)

  #MNIST pictures are 28*28 pixels. Now grad is 28*28 nd.array
  #and x is input image converted to 28*28 nd.array
  for i in range(num):
    for j in range(num):
      plus=copy.deepcopy(x)
      minus=copy.deepcopy(x)

      plus[i*tile:(i+1)*tile , j*tile:(j+1)*tile]=  plus[i*tile:(i+1)*tile , j*tile:(j+1)*tile]+h
      minus[i*tile:(i+1)*tile , j*tile:(j+1)*tile]=  minus[i*tile:(i+1)*tile , j*tile:(j+1)*tile]-h

      plus=np.expand_dims(plus,axis=(0,-1))
      minus=np.expand_dims(minus,axis=(0,-1))
      plus=model(plus)
      minus=model(minus)
      plus=cce(y,plus).numpy()
      minus=cce(y,minus).numpy()
      grad[i*tile:(i+1)*tile , j*tile:(j+1)*tile]=(plus-minus)/2/h

  
  x= tf.convert_to_tensor(np.expand_dims(x_test[idx], axis=0)) 
  with tf.GradientTape() as tape:
    tape.watch(x) 
    y=model(x)
    y_expanded=np.expand_dims(y_train[idx],axis=0)  
    loss=cce(y_expanded,y)

     
  
  delta=tape.gradient(loss,x)
  delta=delta.numpy()
  delta=np.squeeze(delta)


  diff=rel_error(grad,delta)

  print('diff ',diff)
  w()
  #problem : diff is very large. it should be less than 1e-4

Вы можете обратиться здесь для моего полного кода.

1 ответ

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

      import numpy as np
from itertools import product
import matplotlib.pyplot as plt

# make some data
grad = lambda f, x, h: (f(x + h) - f(x-h)) / (2*h*np.ones_like(x))

h = 1e-4

w = np.random.rand(20,20)
f = lambda x: (w*x).sum()
xx, yy = np.meshgrid(*(np.linspace(0,1,20),)*2)
x = np.sin(xx) + np.cos(yy)

# Gradient - you would use numba here to speed up the loop
def gradient(f, x, h):
    
    grad = lambda f, x, hs, h: (f(x + hs) - f(x-hs)) / (2*h)
    
    out = np.zeros_like(x)
    hs = np.zeros_like(x)
    # For a more general solution probably use x.ravel()
    for i in range(x.shape[0]):
        for j in range(x.shape[1]):
            hs *= 0.
            hs[i,j] = h
            out[i,j] = grad(f, x, hs, h)
    
    return out
        
g = gradient(f, x, h)

plt.imshow(x)
plt.show()
plt.imshow(g)
plt.show()

# since f is linear, this should be within numerical accuracy
print(np.allclose(g,w))
Другие вопросы по тегам