TensorFlow에서 경사하강법의 다양한 응용
게시 됨: 2022-03-11Google의 TensorFlow는 딥 러닝 모델을 교육하고 배포하기 위한 최고의 도구 중 하나입니다. 수억 개의 매개변수를 사용하여 매우 복잡한 신경망 아키텍처를 최적화할 수 있으며 하드웨어 가속, 분산 교육 및 프로덕션 워크플로를 위한 다양한 도구와 함께 제공됩니다. 이러한 강력한 기능으로 인해 딥 러닝 영역 밖에서는 위협적이고 불필요한 것처럼 보일 수 있습니다.
그러나 TensorFlow는 딥 러닝 모델 교육과 직접 관련이 없는 더 간단한 문제에 액세스할 수 있고 사용할 수 있습니다. 핵심적으로 TensorFlow는 텐서 연산(벡터, 행렬 등)과 임의의 계산 시퀀스에서 경사하강법을 수행하는 데 사용되는 미적분 연산에 최적화된 라이브러리입니다. 숙련된 데이터 과학자는 "경사 하강법"을 계산 수학의 기본 도구로 인식하지만 일반적으로 응용 프로그램별 코드 및 방정식을 구현해야 합니다. 여기서 살펴보겠지만 TensorFlow의 현대적인 "자동 차별화" 아키텍처가 여기에 있습니다.
TensorFlow 사용 사례
- 예 1: TensorFlow 2.0에서 경사하강법을 사용한 선형 회귀
- 경사하강법이란?
- 예제 2: 최대로 퍼진 단위 벡터
- 예 3: 적대적 AI 입력 생성
- 최종 생각: 경사하강법 최적화
- TensorFlow의 경사하강법: 최소값 찾기에서 AI 시스템 공격까지
예 1: TensorFlow 2.0에서 경사하강법을 사용한 선형 회귀
예 1 노트북
TensorFlow 코드를 사용하기 전에 경사 하강법 및 선형 회귀에 익숙해지는 것이 중요합니다.
경사하강법이란?
가장 간단한 용어로, 출력을 최소화하는 방정식 시스템에 대한 입력을 찾는 수치적 기술입니다. 머신 러닝의 맥락에서 방정식 시스템은 우리의 모델 이고 입력은 모델의 알려지지 않은 매개변수 이며 출력은 최소화할 손실 함수 로 모델과 데이터 사이에 얼마나 많은 오류가 있는지 나타냅니다. 선형 회귀와 같은 일부 문제의 경우 오류를 최소화하는 매개변수를 직접 계산하는 방정식이 있지만 대부분의 실제 응용 프로그램에서는 만족스러운 솔루션에 도달하기 위해 경사하강법과 같은 수치 기술이 필요합니다.
이 기사의 가장 중요한 점은 경사하강법은 일반적으로 방정식을 배치하고 손실 함수와 매개변수 간의 관계를 도출하기 위해 미적분학을 사용해야 한다는 것입니다. TensorFlow(및 모든 최신 자동 미분 도구)를 사용하면 미적분학이 처리되므로 구현에 시간을 할애할 필요 없이 솔루션 설계에 집중할 수 있습니다.
다음은 단순 선형 회귀 문제에서 보이는 것입니다. 우리는 150명의 성인 남성의 키(h)와 몸무게(w)의 샘플을 가지고 있으며 이 선의 기울기와 표준 편차에 대한 불완전한 추측으로 시작합니다. 경사하강법을 약 15번 반복한 후 거의 최적의 솔루션에 도달합니다.
TensorFlow 2.0을 사용하여 위의 솔루션을 어떻게 생성했는지 봅시다.
선형 회귀의 경우 높이의 선형 방정식으로 가중치를 예측할 수 있다고 말합니다.
예측과 실제 값 사이의 평균 제곱 오차(손실)를 최소화하는 매개변수 α 및 β(기울기 및 절편)를 찾고 싶습니다. 따라서 손실 함수 (이 경우 "평균 제곱 오차" 또는 MSE)는 다음과 같습니다.
평균 제곱 오차가 몇 개의 불완전한 선을 찾은 다음 정확한 솔루션을 찾는 방법을 볼 수 있습니다(α=6.04, β=-230.5).
TensorFlow를 사용하여 이 아이디어를 실행해 보겠습니다. 가장 먼저 할 일은 텐서와 tf.*
함수를 사용하여 손실 함수를 코딩하는 것입니다.
def calc_mean_sq_error(heights, weights, slope, intercept): predicted_wgts = slope * heights + intercept errors = predicted_wgts - weights mse = tf.reduce_mean(errors**2) return mse
이것은 매우 간단해 보입니다. 모든 표준 대수 연산자는 텐서에 대해 오버로드되므로 최적화하는 변수가 텐서인지 확인하기만 하면 되며 다른 모든 작업에는 tf.*
메서드를 사용합니다.
그런 다음 우리가 해야 할 일은 이것을 경사 하강 루프에 넣는 것입니다.
def run_gradient_descent(heights, weights, init_slope, init_icept, learning_rate): # Any values to be part of gradient calcs need to be vars/tensors tf_slope = tf.Variable(init_slope, dtype='float32') tf_icept = tf.Variable(init_icept, dtype='float32') # Hardcoding 25 iterations of gradient descent for i in range(25): # Do all calculations under a "GradientTape" which tracks all gradients with tf.GradientTape() as tape: tape.watch((tf_slope, tf_icept)) # This is the same mean-squared-error calculation as before predictions = tf_slope * heights + tf_icept errors = predictions - weights loss = tf.reduce_mean(errors**2) # Auto-diff magic! Calcs gradients between loss calc and params dloss_dparams = tape.gradient(loss, [tf_slope, tf_icept]) # Gradients point towards +loss, so subtract to "descend" tf_slope = tf_slope - learning_rate * dloss_dparams[0] tf_icept = tf_icept - learning_rate * dloss_dparams[1]
이것이 얼마나 깔끔한지 잠시 감상해 봅시다. 경사하강법은 최적화하려는 모든 변수에 대한 손실 함수의 도함수를 계산해야 합니다. 미적분학이 관련되어야 하지만 실제로는 아무 것도 하지 않았습니다. 마법은 다음과 같은 사실에 있습니다.
- TensorFlow는
tf.GradientTape()
에서 수행된 모든 계산의 계산 그래프를 작성합니다. - TensorFlow는 모든 연산의 도함수(기울기)를 계산하는 방법을 알고 있으므로 계산 그래프의 변수가 다른 변수에 미치는 영향을 결정할 수 있습니다.
다른 시작점에서 프로세스가 어떻게 보입니까?
Gradient descent는 최적의 MSE에 놀라울 정도로 가까워지지만 실제로 두 예에서 최적과 실질적으로 다른 기울기와 절편으로 수렴합니다. 어떤 경우에는 이것은 단순히 지역 최소값으로 수렴하는 경사 하강이며, 이는 경사 하강 알고리즘의 고유한 문제입니다. 그러나 선형 회귀는 증명할 수 있는 전역 최소값이 하나만 있습니다. 그렇다면 우리는 어떻게 잘못된 기울기와 절편에 이르렀습니까?
이 경우 문제는 데모를 위해 코드를 지나치게 단순화했다는 것입니다. 우리는 데이터를 정규화하지 않았고, 기울기 매개변수는 절편 매개변수와 다른 특성을 가지고 있습니다. 기울기의 작은 변화는 손실에 큰 변화를 줄 수 있지만 절편의 작은 변화는 거의 영향을 미치지 않습니다. 훈련 가능한 매개변수의 규모에서 이러한 큰 차이는 기울기 계산을 지배하는 기울기로 이어지며 절편 매개변수는 거의 무시됩니다.
따라서 경사 하강법은 초기 절편 추측에 매우 가까운 최상의 기울기를 효과적으로 찾습니다. 그리고 오류가 최적에 매우 가깝기 때문에 주변의 기울기가 작기 때문에 각 연속 반복은 아주 약간만 이동합니다. 먼저 데이터를 정규화하면 이 현상이 크게 개선되었지만 제거되지는 않았을 것입니다.
이것은 비교적 간단한 예였지만 다음 섹션에서 이 "자동 미분" 기능이 꽤 복잡한 것들을 처리할 수 있다는 것을 알게 될 것입니다.
예제 2: 최대로 퍼진 단위 벡터
예 2 노트북
이 다음 예제는 작년에 수강한 딥 러닝 과정의 재미있는 딥 러닝 실습을 기반으로 합니다.
문제의 요점은 32개의 정규 분포 숫자 집합에서 사실적인 얼굴을 생성할 수 있는 "변형 자동 인코더"(VAE)가 있다는 것입니다. 용의자 식별을 위해 VAE를 사용하여 증인이 선택할 다양한 (이론적) 얼굴 세트를 생성한 다음 선택된 얼굴과 유사한 더 많은 얼굴을 생성하여 검색 범위를 좁히고자 합니다. 이 연습에서는 초기 벡터 세트를 무작위화하는 것이 제안되었지만 최적의 초기 상태를 찾고 싶었습니다.
문제를 다음과 같이 표현할 수 있습니다. 32차원 공간이 주어지면 최대로 떨어져 있는 X 단위 벡터 세트를 찾습니다. 2차원에서 이것은 정확하게 계산하기 쉽습니다. 그러나 3차원(또는 32차원!)의 경우 정답은 없습니다. 그러나 목표 상태에 도달했을 때 최소인 적절한 손실 함수를 정의할 수 있다면 경사 하강법이 우리가 거기에 도달하는 데 도움이 될 수 있습니다.
우리는 위에 표시된 대로 20개 벡터의 무작위 세트로 시작하고 TensorFlow의 기능을 보여주기 위해 각각 점점 더 복잡해지는 3개의 다른 손실 함수로 실험할 것입니다.
먼저 훈련 루프를 정의합시다. 우리는 모든 TensorFlow 로직을 self.calc_loss()
메서드 아래에 둘 것이며, 이 루프를 재활용하여 각 기술에 대해 해당 메서드를 간단히 재정의할 수 있습니다.
# Define the framework for trying different loss functions # Base class implements loop, sub classes override self.calc_loss() class VectorSpreadAlgorithm: # ... def calc_loss(self, tensor2d): raise NotImplementedError("Define this in your derived class") def one_iter(self, i, learning_rate): # self.vecs is an 20x2 tensor, representing twenty 2D vectors tfvecs = tf.convert_to_tensor(self.vecs, dtype=tf.float32) with tf.GradientTape() as tape: tape.watch(tfvecs) loss = self.calc_loss(tfvecs) # Here's the magic again. Derivative of spread with respect to # input vectors gradients = tape.gradient(loss, tfvecs) self.vecs = self.vecs - learning_rate * gradients
시도할 첫 번째 기술이 가장 간단합니다. 가장 가까운 벡터의 각도인 확산 메트릭을 정의합니다. 우리는 확산을 최대화하고 싶지만 그것을 최소화 문제로 만드는 것이 관례입니다. 따라서 우리는 단순히 스프레드 메트릭의 음수를 취합니다.
class VectorSpread_Maximize_Min_Angle(VectorSpreadAlgorithm): def calc_loss(self, tensor2d): angle_pairs = tf.acos(tensor2d @ tf.transpose(tensor2d)) disable_diag = tf.eye(tensor2d.numpy().shape[0]) * 2 * np.pi spread_metric = tf.reduce_min(angle_pairs + disable_diag) # Convention is to return a quantity to be minimized, but we want # to maximize spread. So return negative spread return -spread_metric
일부 Matplotlib 마법은 시각화를 생성합니다.
이것은 투박하지만(말 그대로!) 작동합니다. 20개 벡터 중 2개만 한 번에 업데이트되어 더 이상 가장 가깝지 않을 때까지 두 벡터 사이의 공간을 늘린 다음 가장 가까운 새로운 두 벡터 사이의 각도를 늘리도록 전환합니다. 주목해야 할 중요한 점은 그것이 작동 한다는 것입니다. TensorFlow가 tf.reduce_min()
tf.acos()
를 통해 그라디언트를 전달하여 올바른 작업을 수행할 수 있음을 알 수 있습니다.
좀 더 정교한 것을 시도해 보겠습니다. 우리는 최적의 솔루션에서 모든 벡터가 가장 가까운 이웃에 대해 동일한 각도를 가져야 한다는 것을 알고 있습니다. 손실 함수에 "최소 각도의 분산"을 추가해 보겠습니다.
class VectorSpread_MaxMinAngle_w_Variance(VectorSpreadAlgorithm): def spread_metric(self, tensor2d): """ Assumes all rows already normalized """ angle_pairs = tf.acos(tensor2d @ tf.transpose(tensor2d)) disable_diag = tf.eye(tensor2d.numpy().shape[0]) * 2 * np.pi all_mins = tf.reduce_min(angle_pairs + disable_diag, axis=1) # Same calculation as before: find the min-min angle min_min = tf.reduce_min(all_mins) # But now also calculate the variance of the min angles vector avg_min = tf.reduce_mean(all_mins) var_min = tf.reduce_sum(tf.square(all_mins - avg_min)) # Our spread metric now includes a term to minimize variance spread_metric = min_min - 0.4 * var_min # As before, want negative spread to keep it a minimization problem return -spread_metric
가장 가까운 이웃에 대한 각도가 거대하고 현재 최소화되고 있는 분산 항에 스파이크가 발생하기 때문에 고독한 북쪽 벡터는 이제 동료와 빠르게 합류합니다. 그러나 여전히 궁극적으로 증가 속도가 느린 전 세계 최소 각도에 의해 주도됩니다. 이것을 개선해야 하는 아이디어는 일반적으로 이 2D 경우에 작동하지만 더 높은 차원에서는 작동하지 않습니다.
그러나 이 수학적 시도의 질에 너무 많은 초점을 맞추는 것은 요점을 놓치고 있습니다. 평균 및 분산 계산에 얼마나 많은 텐서 연산이 관련되어 있는지, 그리고 TensorFlow가 입력 행렬의 모든 구성요소에 대한 모든 계산을 성공적으로 추적하고 구별하는 방법을 살펴보세요. 그리고 우리는 어떤 수동 계산도 할 필요가 없었습니다. 우리는 몇 가지 간단한 수학을 함께 던졌고 TensorFlow가 우리를 위해 계산을 해주었습니다.
마지막으로 힘 기반 솔루션을 한 가지 더 시도해 보겠습니다. 모든 벡터가 중심점에 연결된 작은 행성이라고 상상해 보세요. 각 행성은 다른 행성에서 그것을 밀어내는 힘을 방출합니다. 이 모델의 물리학 시뮬레이션을 실행하려면 원하는 솔루션에 도달해야 합니다.
내 가설은 경사하강법도 작동해야 한다는 것입니다. 최적의 솔루션에서 다른 모든 행성의 모든 행성에 대한 접선력은 순 0력으로 상쇄되어야 합니다(0이 아닌 경우 행성이 이동할 것입니다). 모든 벡터에 대한 힘의 크기를 계산하고 경사하강법을 사용하여 0으로 밀어봅시다.
먼저 tf.*
방법을 사용하여 힘을 계산하는 방법을 정의해야 합니다.
class VectorSpread_Force(VectorSpreadAlgorithm): def force_a_onto_b(self, vec_a, vec_b): # Calc force assuming vec_b is constrained to the unit sphere diff = vec_b - vec_a norm = tf.sqrt(tf.reduce_sum(diff**2)) unit_force_dir = diff / norm force_magnitude = 1 / norm**2 force_vec = unit_force_dir * force_magnitude # Project force onto this vec, calculate how much is radial b_dot_f = tf.tensordot(vec_b, force_vec, axes=1) b_dot_b = tf.tensordot(vec_b, vec_b, axes=1) radial_component = (b_dot_f / b_dot_b) * vec_b # Subtract radial component and return result return force_vec - radial_component
그런 다음 위의 force 함수를 사용하여 손실 함수를 정의합니다. 우리는 각 벡터에 순 힘을 축적하고 그 크기를 계산합니다. 최적의 솔루션에서는 모든 힘이 상쇄되어야 하고 힘이 0이어야 합니다.

def calc_loss(self, tensor2d): n_vec = tensor2d.numpy().shape[0] all_force_list = [] for this_idx in range(n_vec): # Accumulate force of all other vecs onto this one this_force_list = [] for other_idx in range(n_vec): if this_idx == other_idx: continue this_vec = tensor2d[this_idx, :] other_vec = tensor2d[other_idx, :] tangent_force_vec = self.force_a_onto_b(other_vec, this_vec) this_force_list.append(tangent_force_vec) # Use list of all N-dimensional force vecs. Stack and sum. sum_tangent_forces = tf.reduce_sum(tf.stack(this_force_list)) this_force_mag = tf.sqrt(tf.reduce_sum(sum_tangent_forces**2)) # Accumulate all magnitudes, should all be zero at optimal solution all_force_list.append(this_force_mag) # We want to minimize total force sum, so simply stack, sum, return return tf.reduce_sum(tf.stack(all_force_list))
솔루션이 아름답게 작동할 뿐만 아니라(처음 몇 프레임의 혼란을 제외하고) 실제 크레딧은 TensorFlow에 있습니다. 이 솔루션에는 여러 for
루프, if
문, 거대한 계산 웹이 포함되었으며 TensorFlow는 우리를 위해 이 모든 것을 통해 성공적으로 그라디언트를 추적했습니다.
예 3: 적대적 AI 입력 생성
예 3 노트북
이쯤 되면 독자들은 "이봐! 이 글은 딥러닝에 대한 글이 아니었어!"라고 생각할 수도 있다. 그러나 기술적으로 도입은 "딥 러닝 모델 교육 "을 넘어서는 것을 의미합니다. 이 경우 우리는 훈련 이 아니라 사전 훈련된 심층 신경망의 일부 수학적 속성을 이용하여 잘못된 결과를 제공하도록 속입니다. 이것은 상상했던 것보다 훨씬 쉽고 효과적인 것으로 판명되었습니다. TensorFlow 2.0 코드의 또 다른 짧은 덩어리만 있으면 됩니다.
공격할 이미지 분류기를 찾는 것으로 시작합니다. 우리는 Dogs vs. Cats Kaggle Competition에서 최고의 솔루션 중 하나를 사용할 것입니다. 특히 Kaggler가 제시한 솔루션은 "uysimty"입니다. 효과적인 cat-vs-dog 모델을 제공하고 훌륭한 문서를 제공한 데 대한 모든 크레딧입니다. 이것은 18개의 신경망 계층에 걸쳐 1,300만 개의 매개변수로 구성된 강력한 모델입니다. (독자는 해당 노트북에서 이에 대한 자세한 내용을 읽을 수 있습니다.)
여기서 목표는 이 특정 네트워크의 결함을 강조하는 것이 아니라 입력이 많은 표준 신경망이 얼마나 취약한지를 보여주는 것입니다.
약간의 수정으로 모델을 로드하고 분류할 이미지를 사전 처리하는 방법을 알아낼 수 있었습니다.
이것은 정말 견고한 분류기처럼 보입니다! 모든 샘플 분류는 정확하고 95% 이상의 신뢰도를 가지고 있습니다. 공격하자!
우리는 분명히 고양이인 이미지를 생성하고 싶지만 분류자가 높은 신뢰도로 개라고 결정하도록 합니다. 어떻게 할 수 있습니까?
올바르게 분류하는 고양이 사진으로 시작하여 주어진 입력 픽셀의 각 색상 채널(값 0-255)의 작은 수정이 최종 분류기 출력에 어떤 영향을 미치는지 알아보겠습니다. 하나의 픽셀을 수정하는 것은 별로 도움이 되지 않을 것입니다. 하지만 모든 128x128x3 = 49,152 픽셀 값의 누적 조정은 우리의 목표를 달성할 것입니다.
각 픽셀을 푸시하는 방법을 어떻게 알 수 있습니까? 정상적인 신경망 훈련 중에 TensorFlow에서 경사 하강법을 사용하여 1,300만 개의 무료 매개변수를 모두 동시에 업데이트하여 대상 레이블과 예측된 레이블 간의 손실을 최소화하려고 합니다. 이 경우 대신 1,300만 매개변수를 고정된 상태로 두고 입력 자체의 픽셀 값을 조정합니다.
우리의 손실 함수는 무엇입니까? 글쎄, 그것은 이미지가 얼마나 고양이처럼 보이는지입니다! 각 입력 픽셀에 대한 cat 값의 도함수를 계산하면 고양이 분류 확률을 최소화하기 위해 각 픽셀을 푸시하는 방법을 알 수 있습니다.
def adversarial_modify(victim_img, to_dog=False, to_cat=False): # We only need four gradient descent steps for i in range(4): tf_victim_img = tf.convert_to_tensor(victim_img, dtype='float32') with tf.GradientTape() as tape: tape.watch(tf_victim_img) # Run the image through the model model_output = model(tf_victim_img) # Minimize cat confidence and maximize dog confidence loss = (model_output[0] - model_output[1]) dloss_dimg = tape.gradient(loss, tf_victim_img) # Ignore gradient magnitudes, only care about sign, +1/255 or -1/255 pixels_w_pos_grad = tf.cast(dloss_dimg > 0.0, 'float32') / 255. pixels_w_neg_grad = tf.cast(dloss_dimg < 0.0, 'float32') / 255. victim_img = victim_img - pixels_w_pos_grad + pixels_w_neg_grad
Matplotlib 마법은 다시 결과를 시각화하는 데 도움이 됩니다.
와! 인간의 눈에는 이 사진들 각각이 동일합니다. 그러나 4번의 반복 후에 우리는 99.4%의 신뢰도로 분류기를 개라고 확신했습니다!
이것이 우연이 아니며 다른 방향으로도 작동하는지 확인합시다.
성공! 분류기는 원래 98.4%의 신뢰도로 개로 이것을 정확하게 예측했지만 지금은 99.8%의 신뢰도로 고양이라고 믿습니다.
마지막으로 샘플 이미지 패치를 보고 어떻게 바뀌었는지 봅시다.
예상대로 최종 패치는 원본과 매우 유사하며 각 픽셀은 빨간색 채널의 강도 값에서 -4에서 +4로만 이동합니다. 이 변화는 인간이 그 차이를 구별하기에 충분하지 않지만 분류기의 출력을 완전히 변경합니다.
최종 생각: 경사하강법 최적화
이 기사 전체에서 단순성과 투명성을 위해 훈련 가능한 매개변수에 수동으로 그라디언트를 적용하는 방법을 살펴보았습니다. 그러나 실제 세계에서 데이터 과학자는 코드 부풀림을 추가하지 않고 훨씬 더 효과적인 경향이 있기 때문에 옵티마이저 를 사용하는 데 바로 뛰어들어야 합니다.
RMSprop, Adagrad 및 Adadelta를 포함하여 널리 사용되는 최적화 프로그램이 많이 있지만 가장 일반적인 것은 아마도 Adam 입니다. 때로는 각 매개변수에 대해 다른 학습률을 동적으로 유지하기 때문에 "적응 학습률 방법"이라고 합니다. 그들 중 대부분은 극소값을 탈출하고 더 빠른 수렴을 달성하기 위해 모멘텀 항과 대략적인 고차 도함수를 사용합니다.
Sebastian Ruder에서 빌린 애니메이션에서 다양한 옵티마이저가 손실 표면을 내려가는 경로를 볼 수 있습니다. 우리가 시연한 수동 기술은 "SGD"와 가장 유사합니다. 최고 성능의 옵티마이저는 모든 손실 표면에 대해 동일한 옵티마이저가 아닙니다. 그러나 고급 옵티마이 저는 일반적으로 단순한 옵티마이저보다 성능이 좋습니다.
그러나 인공 지능 개발 서비스를 제공하는 데 열심인 사람들에게도 최적화 도구의 전문가는 거의 유용하지 않습니다. 개발자가 TensorFlow에서 경사 하강법을 개선하는 방법을 이해하기 위해 몇 가지에 익숙해지는 것이 개발자의 시간을 더 잘 활용하는 것입니다. 그 후에는 기본적으로 Adam 을 사용하고 모델이 수렴되지 않는 경우에만 다른 것을 시도할 수 있습니다.
이러한 옵티마이저가 작동하는 방법과 이유에 정말로 관심이 있는 독자를 위해 애니메이션이 나타나는 Ruder의 개요는 해당 주제에 대한 가장 훌륭하고 포괄적인 리소스 중 하나입니다.
최적화 프로그램을 사용하도록 첫 번째 섹션의 선형 회귀 솔루션을 업데이트해 보겠습니다. 다음은 수동 그래디언트를 사용하는 원래 그래디언트 하강 코드입니다.
# Manual gradient descent operations def run_gradient_descent(heights, weights, init_slope, init_icept, learning_rate): tf_slope = tf.Variable(init_slope, dtype='float32') tf_icept = tf.Variable(init_icept, dtype='float32') for i in range(25): with tf.GradientTape() as tape: tape.watch((tf_slope, tf_icept)) predictions = tf_slope * heights + tf_icept errors = predictions - weights loss = tf.reduce_mean(errors**2) gradients = tape.gradient(loss, [tf_slope, tf_icept]) tf_slope = tf_slope - learning_rate * gradients[0] tf_icept = tf_icept - learning_rate * gradients[1]
이제 옵티마이저를 대신 사용하는 동일한 코드가 있습니다. 추가 코드가 거의 없음을 알 수 있습니다(변경된 줄은 파란색으로 강조 표시됨).
# Gradient descent with Optimizer (RMSprop) def run_gradient_descent (heights, weights, init_slope, init_icept, learning_rate) : tf_slope = tf.Variable(init_slope, dtype= 'float32' ) tf_icept = tf.Variable(init_icept, dtype= 'float32' ) # Group trainable parameters into a list trainable_params = [tf_slope, tf_icept] # Define your optimizer (RMSprop) outside of the training loop optimizer = keras.optimizers.RMSprop(learning_rate) for i in range( 25 ): # GradientTape loop is the same with tf.GradientTape() as tape: tape.watch( trainable_params ) predictions = tf_slope * heights + tf_icept errors = predictions - weights loss = tf.reduce_mean(errors** 2 ) # We can use the trainable parameters list directly in gradient calcs gradients = tape.gradient(loss, trainable_params ) # Optimizers always aim to *minimize* the loss function optimizer.apply_gradients(zip(gradients, trainable_params))
그게 다야! 기울기 하강 루프 외부에서 RMSprop
옵티마이저를 정의한 다음 각 기울기 계산 후에 optimizer.apply_gradients()
메서드를 사용하여 훈련 가능한 매개변수를 업데이트했습니다. 옵티마이저는 운동량 및 고차 도함수와 같은 추가 항을 계산하기 위해 과거 기울기를 추적하기 때문에 루프 외부에서 정의됩니다.
RMSprop 옵티마이저로 어떻게 보이는지 봅시다.
좋아 보인다! 이제 Adam 옵티마이저로 시도해 보겠습니다.
와, 여기서 무슨 일이? Adam의 운동량 역학으로 인해 최적의 솔루션을 오버슈트하고 여러 번 코스를 역전시키는 것으로 보입니다. 일반적으로 이 운동량 역학은 복잡한 손실 표면에 도움이 되지만 이 간단한 경우에는 문제가 됩니다. 이는 모델을 훈련할 때 조정할 하이퍼파라미터 중 하나를 최적화 프로그램으로 선택하라는 조언을 강조합니다.
딥 러닝을 탐색하려는 사람은 누구나 이 패턴에 익숙해지기를 원할 것입니다. 이 패턴은 표준 워크플로에 쉽게 포함되지 않는 복잡한 손실 메커니즘이 필요한 사용자 지정 TensorFlow 아키텍처에서 광범위하게 사용되기 때문입니다. 이 간단한 TensorFlow 경사하강법 예제에서는 훈련 가능한 매개변수가 두 개뿐이었지만 최적화하려면 수억 개의 매개변수가 포함된 아키텍처로 작업할 때 필요합니다.
TensorFlow의 경사하강법: 최소값 찾기에서 AI 시스템 공격까지
모든 코드 조각과 이미지는 해당 GitHub 리포지토리의 노트북에서 생성되었습니다. 또한 전체 코드를 보고자 하는 독자를 위해 개별 노트북에 대한 링크와 함께 모든 섹션의 요약이 포함되어 있습니다. 메시지를 단순화하기 위해 광범위한 인라인 문서에서 찾을 수 있는 많은 세부 정보가 생략되었습니다.
이 기사가 통찰력이 있고 TensorFlow에서 경사 하강법을 사용하는 방법에 대해 생각하는 데 도움이 되었기를 바랍니다. 직접 사용하지 않더라도 모든 최신 신경망 아키텍처가 작동하는 방식(모델 생성, 손실 함수 정의, 경사하강법 사용)을 사용하여 모델을 데이터 세트에 맞추는 방식이 더 명확해지기를 바랍니다.
Google Cloud 파트너로서 Toptal의 Google 인증 전문가는 회사의 가장 중요한 프로젝트에 대한 수요가 있을 때 사용할 수 있습니다.