인공지능
합성곱(3)
yul_S2
2023. 1. 18. 09:48
반응형
ReLu
합성곱층에 적용되는 활성화 함수
합성곱 신경망이 성능을 더 높임
0보다 큰값은 통과
0보다 작은값은 0
# 1. ReLu함수 구현
def relu(x) : return np.maximum(x,0) |
▶ numpy의 maximum함수 사용 : 두 매개값중 가장큰값을 골라 반환
# 1-1) 확인
x=np.array([-1,2,-3,4,-5]) relu(x) |
# array([0, 2, 0, 4, 0]) |
▶ 음수는 0이된다
# 1-2) 텐서플로의 relu
r_out = tf.nn.relu(x) r_out.numpy() |
# array([0, 2, 0, 4, 0]) |
▶ 텐서플로는 tensor 객체를 반환하므로 numpy로 변환해야한다
# 2. ReLu함수의 도함수
▶ 0보다 크면 1이고 0보다 작으면0이다
*합성곱 신경망은 이미지의 2차원 배열을 펼칠 필요가 없다 -> 이미지정보가 손상되지 않는다.
채널이라는 차원을 하나 더 가진다.
특성맵의 마지막차원을 채널이라고한다.
이미지의 모든채널에 합성곱이 한번에 적용되어야 하므로
커널의 마지막차원은 입력채널의 개수와 동일해야한다.
합성곱신경망구조
ConvolutionNetwork
합성곱, relu함수, 풀링적용
합성곱과 풀링이 일어나는 정방향 계산 : forpass
glorot_uniform
가중치를 초기화할때 글로럿 초기화라는 방법을 사용할 수 있게한다.
*신경망 모델이 너무 커지면 손실 함수도 복잡해지기 때문에 출발점에 따라 결과가 달라질 수 있다.
# MultiClassNetwork 클래스
forpass() 메서드에 있던 z1, a1, z2를 계산하는 식은 그대로 두고 그 앞에 합성곱과 풀링층을 추가
import tensorflow as tf class ConvolutionNetwork: def __init__(self, n_kernels=10, units=10, batch_size=32, learning_rate=0.1): self.n_kernels = n_kernels # 합성곱의 커널 개수 : 만들어진 특성맵의 커널 수 self.kernel_size = 3 # 커널 크기 self.optimizer = None # 옵티마이저 self.conv_w = None # 합성곱 층의 가중치 self.conv_b = None # 합성곱 층의 절편 self.units = units # 은닉층의 뉴런 개수 self.batch_size = batch_size # 배치 크기 self.w1 = None # 은닉층의 가중치 self.b1 = None # 은닉층의 절편 self.w2 = None # 출력층의 가중치 self.b2 = None # 출력층의 절편 self.a1 = None # 은닉층의 활성화 출력 self.losses = [] # 훈련 손실 self.val_losses = [] # 검증 손실 self.lr = learning_rate # 학습률 def forpass(self, x): # 3x3 합성곱 연산을 수행합니다.,self.conv_w : 합성곱에 사용할 가중치 c_out = tf.nn.conv2d(x, self.conv_w, strides=1, padding='SAME') + self.conv_b # 렐루 활성화 함수를 적용합니다. r_out = tf.nn.relu(c_out) #★ 2x2 최대 풀링을 적용합니다. p_out = tf.nn.max_pool2d(r_out, ksize=2, strides=2, padding='VALID') #★ 첫 번째 배치 차원을 제외하고 출력을 일렬로 펼칩니다. f_out = tf.reshape(p_out, [x.shape[0], -1]) #★ np.dot -> tf.matmul 로 변경 : conv2d, max_pool2d가 tensor객체를 반환하기때문 z1 = tf.matmul(f_out, self.w1) + self.b1 # 첫 번째 층의 선형 식을 계산합니다 a1 = tf.nn.relu(z1) # 활성화 함수를 적용합니다 z2 = tf.matmul(a1, self.w2) + self.b2 # 두 번째 층의 선형 식을 계산합니다. return z2 """ ★ glorot_uniform 함수로 초기화한다. 점과 텐서플로의 자동 미분 기능을 사용하기 위해 가중치를 tf.Variable 함수로 만든다 : 입력값에 따라 자동으로 자료형이 결정된다. np.zeros 함수는 기본적으로 64비트 실수를 만든다. -> 절변 변수와 가중치 변수가 동일하게 32비트 실수로 맞추기 위해 dtype매개변수에 float지정하였다. """ def init_weights(self, input_shape, n_classes): g = tf.initializers.glorot_uniform() #★ glorot 방식으로 초기화 : glorot_uniform()함수에서 만든 객체를 호출할때 필요한 가중치 크기를 전달하면된다 self.conv_w = tf.Variable(g((3, 3, 1, self.n_kernels))) self.conv_b = tf.Variable(np.zeros(self.n_kernels), dtype=float) n_features = 14 * 14 * self.n_kernels # ★ glorot 방식으로 초기화 self.w1 = tf.Variable(g((n_features, self.units))) # (특성 개수, 은닉층의 크기) self.b1 = tf.Variable(np.zeros(self.units), dtype=float) # 은닉층의 크기 # ★ glorot 방식으로 초기화 self.w2 = tf.Variable(g((self.units, n_classes))) # (은닉층의 크기, 클래스 개수) self.b2 = tf.Variable(np.zeros(n_classes), dtype=float) # 클래스 개수 def fit(self, x, y, epochs=100, x_val=None, y_val=None): self.init_weights(x.shape, y.shape[1]) # 은닉층과 출력층의 가중치를 초기화합니다. self.optimizer = tf.optimizers.SGD(learning_rate=self.lr) # epochs만큼 반복합니다. for i in range(epochs): print('에포크', i, end=' ') # 제너레이터 함수에서 반환한 미니배치를 순환합니다. batch_losses = [] for x_batch, y_batch in self.gen_batch(x, y): print('.', end='') self.training(x_batch, y_batch) # 배치 손실을 기록합니다. batch_losses.append(self.get_loss(x_batch, y_batch)) print() # 배치 손실 평균내어 훈련 손실 값으로 저장합니다. self.losses.append(np.mean(batch_losses)) # 검증 세트에 대한 손실을 계산합니다. self.val_losses.append(self.get_loss(x_val, y_val)) # 미니배치 제너레이터 함수 def gen_batch(self, x, y): bins = len(x) // self.batch_size # 미니배치 횟수 indexes = np.random.permutation(np.arange(len(x))) # 인덱스를 섞습니다. x = x[indexes] y = y[indexes] for i in range(bins): start = self.batch_size * i end = self.batch_size * (i + 1) yield x[start:end], y[start:end] # batch_size만큼 슬라이싱하여 반환합니다. #★ backprop메서드를 구현할 필요가 없음 : 자동미분기능을 사용하기때문 def training(self, x, y): m = len(x) # 샘플 개수를 저장합니다. with tf.GradientTape() as tape: z = self.forpass(x) # 정방향 계산을 수행합니다. #★ 정방향계산의 결과(z), 타깃(y)을 기반으로 손실을 계산합니다. loss = tf.nn.softmax_cross_entropy_with_logits(y, z) #★ 배치의 각 샘플에 대한 손실을 반환하므로 reduce_mean함수로 평균을 계산 loss = tf.reduce_mean(loss) weights_list = [self.conv_w, self.conv_b, self.w1, self.b1, self.w2, self.b2] #★ 그레이디언트 자동계산 tape.gradient # : 가중치에 대한 그래디언트를 계산합니다. grads = tape.gradient(loss, weights_list) #★ 그레이디언트와 가중치를 튜플로 묶은 리스트를 전달 apply_gradients # : python의 zip반복자 사용 : 가중치를 업데이트합니다. self.optimizer.apply_gradients(zip(grads, weights_list)) """ prdict메서드는 정방향 계산으로 얻은 출력값인 tensor객체를 넘파이 배열로 바꾼다. """ def predict(self, x): z = self.forpass(x) # 정방향 계산을 수행합니다. return np.argmax(z.numpy(), axis=1) # 가장 큰 값의 인덱스를 반환합니다. def score(self, x, y): # 예측과 타깃 열 벡터를 비교하여 True의 비율을 반환합니다. return np.mean(self.predict(x) == np.argmax(y, axis=1)) """ softmax_cross_entropy_with_logits 함수를 사용하는 get_loss함수로 계산한다. """ def get_loss(self, x, y): z = self.forpass(x) # 정방향 계산을 수행합니다. # 손실을 계산하여 저장합니다. loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(y, z)) return loss.numpy() |
반응형