59doit

합성곱(4) 본문

인공지능

합성곱(4)

yul_S2 2023. 1. 18. 09:49
반응형

 MNIST 데이터 세트 활용 

 

# 데이터 세트 불러오기

(x_train_all, y_train_all),(x_test,y_test) = tf.keras.datasets.fashion_mnist.load_data()

▶ 

 

 

 

# 훈련데이터세트를 훈련세트와 검증세트로 나누기

from sklearn.model_selection import train_test_split
x_train, x_val, y_train, y_val = train_test_split(x_train_all,y_train_all,
                                                  stratify=y_train_all, test_size=0.2,random_state=42)
x_train.shape # (48000, 28, 28)
x_val.shape # (12000, 28, 28)

▶ 

 

 

 

 

# 원 핫 인코딩 변환

y_train_encoded = tf.keras.utils.to_categorical(y_train)
y_val_encoded = tf.keras.utils.to_categorical(y_val)

▶ 

 

 

 

 

# 입력데이터 준비

x_train = x_train.reshape(-1,28,28,1)
x_val = x_val.reshape(-1,28,28,1)

▶ 합성곱 신경망은 입력데이터를 일렬로 펼칠 필요가 없다.
높이와 너비 차원만 그대로 유지하여 신경망에 주입하고 마지막에 채널추가해야한다.
넘파이 reshape 메서드 사용하면 마지막 차원을 추가할 수 있다.

 

 

 

 

# 마지막 차원 추가되었는지 확인

x_train.shape # (48000, 28, 28, 1)
x_val.shape # (12000, 28, 28, 1)

 

 

# 입력데이터 표준화 전처리

x_train = x_train/255
x_val = x_val/255

 

 

# 모델 훈련하기

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
   
    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))

    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))
 
    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()
cn = ConvolutionNetwork(n_kernels=10, units=100, batch_size=128, learning_rate=0.01)
cn.fit(x_train, y_train_encoded, x_val=x_val, y_val=y_val_encoded, epochs=10)

▶ ConvolutionNetwork 클래스 객체 생성 
fit메서드 호출하여 모델 훈련
합성곱 커널 10개 사용 : n_kernels
완전 연결츨 뉴런 100개 사용 : units
배치 크기 128개 : batch_size
학습률 0.01 지정 : learning_rate
epoch 20 동안 훈련 : epoch

 

 

# 손실함수확인

import matplotlib.pyplot as plt
plt.plot(cn.losses)
plt.plot(cn.val_losses)
plt.ylabel('loss')
plt.xlabel('iteration')
plt.legend(['train_loss','val_loss'])
plt.show()

▶ 

 

 

 

# 정확도

cn.score(x_val,y_val_encoded)
# 0.8596666666666667

 

 

 

반응형

'인공지능' 카테고리의 다른 글

순환신경망 (1)  (5) 2023.01.21
합성곱 (6)  (2) 2023.01.21
합성곱(3)  (2) 2023.01.18
합성곱(2)  (4) 2023.01.17
합성곱(1)  (1) 2023.01.17
Comments