
본 자료는 이수안 교수님(https://suanlab.com/)의 자료를 일부 수정 후 업데이트 한 자료입니다.
ipynb 나 pdf 자료가 필요하신분은 연락주세요.

신경세포(Neuron)
인공 뉴런(Artificial Neuron)
인공 신경망(Artificial Neural Network)
가장 널리 쓰이는 딥러닝 프레임워크 중 하나
구글이 주도적으로 개발하는 플랫폼
파이썬, C++ API를 기본적으로 제공하고,
자바스크립트(JavaScript), 자바(Java), 고(Go), 스위프트(Swift) 등 다양한 프로그래밍 언어를 지원
tf.keras를 중심으로 고수준 API 통합 (2.x 버전)
TPU(Tensor Processing Unit) 지원

import tensorflow as tf
import numpy as np
t0 = tf.constant(1)
print(t0)
print(tf.rank(t0)) # 축이 없는(0) 상태
t1 = tf.constant([1,2,3])
print(t1)
print(tf.rank(t1))
t2 = tf.constant([[1,2,3],[4,5,6],[7,8,9]])
print(t2)
print(tf.rank(t2))
#tf.Tensor(
#[[1 2 3]
# [4 5 6]
# [7 8 9]], shape=(3, 3), dtype=int32)
#tf.Tensor(2, shape=(), dtype=int32)
t3 = tf.constant([[[1,2,3],
[4,5,6],
[7,8,9]],
[[1,2,3],
[4,5,6],
[7,8,9]],
[[1,2,3],
[4,5,6],
[7,8,9]]])
print(t3)
print(tf.rank(t3)) # 축이 없는(0) 상태
output :
tf.Tensor(
[[[1 2 3][4 5 6]
[7 8 9]]
[[1 2 3][4 5 6]
[7 8 9]]
[[1 2 3][4 5 6]
[7 8 9]]], shape=(3, 3, 3), dtype=int32)
tf.Tensor(3, shape=(), dtype=int32)
int32float32stringint32, float32, string 타입 외에도 float16, int8 타입 등이 존재tf.cast() 사용i = tf.constant(2)
print(i)
output : tf.Tensor(2, shape=(), dtype=int32)
# tf.constant(2.)는 실수형 텐서를 생성
# 기본 dtype은 float32tf.Tensor(2.0, shape=(), dtype=float32)
i = tf.constant(2.)
print(i)
output : tf.Tensor(2.0, shape=(), dtype=float32)
# tf.constant('ms')는 문자열 텐서를 생성
# 기본 dtype은 string
s = tf.constant('ms')
print(s) # b : 해당 문자열이 바이트(byte) 형식
output : tf.Tensor(b'ms', shape=(), dtype=string)
f16 = tf.constant(2., dtype=tf.float16)
print(f16)
output : tf.Tensor(2.0, shape=(), dtype=float16)
i8 = tf.constant(2, dtype=tf.int8)
print(i)
output : tf.Tensor(2.0, shape=(), dtype=float32)
f32 = tf.cast(f16, tf.float32)
print(f32)
output : tf.Tensor(2.0, shape=(), dtype=float32)
i32 = tf.cast(i8, tf.int32)
print(i32)
output : tf.Tensor(2, shape=(), dtype=int32)
# 텐서 연산 예제
# tf.constant()를 사용하여 상수 텐서를 생성하고 기본적인 사칙연산을 수행
# 덧셈과 뺄셈은 + 와 - 연산자 또는 tf.add()와 tf.subtract() 함수를 사용할 수 있음
print(tf.constant(2) + tf.constant(2))
print(tf.constant(2) - tf.constant(2))
print(tf.add(tf.constant(2) , tf.constant(2)))
print(tf.subtract(tf.constant(2) , tf.constant(2)))
output :
tf.Tensor(4, shape=(), dtype=int32)
tf.Tensor(0, shape=(), dtype=int32)
tf.Tensor(4, shape=(), dtype=int32)
tf.Tensor(0, shape=(), dtype=int32)
print(tf.constant(2) * tf.constant(2))
print(tf.constant(2) / tf.constant(2))
print(tf.multiply(tf.constant(2) , tf.constant(2)))
print(tf.divide(tf.constant(2) , tf.constant(2)))
output :
print(tf.constant(2) * tf.constant(2))
print(tf.constant(2) / tf.constant(2))
print(tf.multiply(tf.constant(2) , tf.constant(2)))
print(tf.divide(tf.constant(2) , tf.constant(2)))
# print(tf.constant(2) + tf.constant(2.2)) # 에러남
print(tf.cast(tf.constant(2),tf.float32) + tf.constant(2.2))
output : tf.Tensor(4.2, shape=(), dtype=float32)
# tensorflow.keras.layers에서 주요 레이어들을 import
# Dense: 완전연결계층을 구현하는 레이어
# Activation: 활성화 함수를 적용하는 레이어
# Flatten: 다차원 입력을 1차원으로 펼치는 레이어
# Input: 모델의 입력을 정의하는 레이어
from tensorflow.keras.layers import Dense, Activation, Flatten, Input
완전연결계층(Fully-Connected Layer)
노드수(유닛수), 활성화 함수(activation) 등을 지정
name을 통한 레이어간 구분 가능
가중치 초기화(kernel_initializer)
kernel_initializer 인자를 통해 다른 가중치 초기화 지정 가능# 아래 레이어를 통과하면 결과로 10개의 출력 노드가 생성됨.
# Dense 레이어는 이전 레이어의 출력이 어떤 크기든 간에
# 이를 받아들여 내부적으로 적절히 처리하고 10개의 출력을 생성
# 레이어를 정의할 때 입력 노드의 수를 명시적으로 지정할 필요는 없음.
Dense(10, activation='softmax')
Dense(10, activation='relu', name='Dense Layer')
Dense(10, kernel_initializer='he_normal', name='Dense Layer')

# 사용 예시
dense = Dense(10,activation='relu',name='Dense Layer')
Activation(dense)
# 전체 출력은 (batch_size, height * width * channels) 형태를 가짐.
# 여기서 첫 번째 차원은 배치 크기를 나타내고, 두 번째 차원은 평탄화된 피처를 나타냄.
# 따라서, Flatten 레이어의 출력 텐서는 랭크가 2인 2D 텐서임.
flatten = Flatten(input_shape=(128,3,2,2))
### Input
- 모델의 입력을 정의
- `shape`, `dtype`을 포함
- 하나의 모델은 여러 개의 입력을 가질 수 있음
- `summary()` 메소드를 통해서는 보이지 않음
# None: 이 위치의 None은 배치 크기(batch size)를 나타냅니다.
# None이 사용된 이유는 배치 크기가 미리 정의되지 않았고,
# 모델을 실행할 때 어떤 배치 크기도 사용될 수 있음을 의미함.
# 즉, 입력 데이터의 총 수는 가변적이며, 실제 모델 훈련이나 추론시에 결정됩니다.
Input(shape=(8,), dtype=tf.int32)
Sequential()add()를 이용한 방법from tensorflow.keras.layers import Dense, Input, Flatten
from tensorflow.keras.models import Sequential, Model
from tensorflow.keras.utils import plot_model
model = Sequential()
model.add(Input(shape=(28,28)))
model.add(Dense(300, activation='relu'))
model.add(Dense(100, activation='relu'))
model.add(Dense(10, activation='softmax'))
model.summary()
output :

Param = 가중치(weight, biases)의 총 수
가중치의 총 수 = (입력 유닛 수) x (출력 유닛 수)
바이어스의 총 수 = 출력 유닛 수(각 출력 유닛마다 하나의 바이어스가 있음)
총 파라미터 수 = 가중치의 총 수 + 바이어스의 총 수
dense_4 = 28300+300
dense_5 = 300100+100
dense_6 = 100*10+10
#!pip install pydot
plot_model(model)

model = Sequential([
Input(shape=(28,28)),
Dense(300, activation='relu'),
Dense(100, activation='relu'),
Dense(10, activation='softmax')])
model.summary()

inputs = Input(shape=(28,28,1))
x = Flatten(input_shape=(28,28,1))(inputs)
x = Dense(300,activation='relu')(x)
x = Dense(100,activation='relu')(x)
x = Dense(10,activation='softmax')(x)
model = Model(inputs=inputs, outputs=x)
model.summary()

plot_model(model)

# 복잡한 모델
from tensorflow.keras.layers import Concatenate
input_layer = Input(shape=(28,28))
hidden1 = Dense(100, activation='relu')(input_layer)
hidden2 = Dense(30, activation='relu')(hidden1)
concat = Concatenate()([input_layer, hidden2])
output = Dense(1)(concat)
model = Model(inputs=[input_layer], outputs=[output])
model.summary()

plot_model(model)

# input 두개인 모델
input_1 = Input(shape=(10,10))
input_2 = Input(shape=(10,28))
hidden1 = Dense(100, activation='relu')(input_2)
hidden2 = Dense(10, activation='relu')(hidden1)
concat = Concatenate()([input_1, hidden2])
output = Dense(1, activation='sigmoid')(concat)
model = Model(inputs=[input_1, input_2], outputs=[output])
model.summary()

plot_model(model)

# input 두개인 모델
input_ = Input(shape=(10,10), name = 'input_')
hidden1 = Dense(100, activation='relu')(input_)
hidden2 = Dense(10, activation='relu')(hidden1)
output = Dense(1, activation='sigmoid', name='main_output')(hidden2)
sub_output = Dense(1, name='sub_output')(hidden2)
model = Model(inputs=[input_], outputs=[output, sub_output])
model.summary()

plot_model(model)

# input, output 둘 다 두개인 모델
input_1 = Input(shape=(10,10))
input_2 = Input(shape=(10,28))
hidden1 = Dense(100, activation='relu')(input_2)
hidden2 = Dense(10, activation='relu')(hidden1)
concat = Concatenate()([input_1, hidden2])
output = Dense(1, activation='sigmoid',name='main_output')(concat)
sub_out = Dense(1,name='sub_output')(hidden2)
model = Model(inputs=[input_1, input_2], outputs=[output, sub_out])
model.summary()

plot_model(model ,show_layer_names=True, show_shapes=True)

#### 서브클래싱(Subclassing)
- 커스터마이징에 최적화된 방법
- 이미 어느정도 만들어진걸 조금 수정하여 재활용하는 느낌
- Model 클래스를 상속받아 Model이 포함하는 기능을 사용할 수 있음
- `fit()`, `evaluate()`, `predict()`
- `save()`, `load()`
- 주로 `call()` 메소드안에서 원하는 계산 가능
- for, if, 저수준 연산 등
- 권장되는 방법은 아니지만 어떤 모델의 구현 코드를 참고할 때, 해석할 수 있어야함
class MyModel(Model):
def __init__(self, units=30, activation='relu',**kwargs):
# Model class의 생성자 실행. Model class에 정의된 value 속성도 초기화
super(MyModel, self).__init__(**kwargs)
#super().__init__(**kwargs) # 이렇게 가능
self.dense_layer1 = Dense(300, activation=activation)
self.dense_layer2 = Dense(100, activation=activation)
self.dense_layer3 = Dense(units, activation=activation)
self.output_layer = Dense(10, activation='softmax')
def call(self, inputs):
x = self.dense_layer1(x)
x = self.dense_layer2(x)
x = self.dense_layer3(x)
x = self.output_layer(x)
return x
model = MyModel()
# 참고
# def abc(**kwargs):
# print(kwargs)
# abc(a=3, b='c') # {'a': 3, 'b': 'c'}
inputs = Input(shape=(28,28,1))
x = Flatten(input_shape=(28,28,1))(inputs)
x = Dense(300, activation='relu')(x)
x = Dense(100, activation='relu')(x)
x = Dense(10, activation='softmax')(x)
model = Model(inputs=inputs, outputs = x)
model.summary()

model.layers
output:
[<InputLayer name=input_layer_26, built=True>,
<Flatten name=flatten_5, built=True>,
<Dense name=dense_73, built=True>,
<Dense name=dense_74, built=True>,
<Dense name=dense_75, built=True>]
hidden_2 = model.layers[2]
hidden_2.name
output : 'dense_73'
# 가져온 모델 맞는지 검증하기
model.get_layer('dense_73') == hidden_2
output : True
# 레이어의 weight, bias 확인하기
weights, biases = hidden_2.get_weights()
print(weights.shape)
print(biases.shape)
output :
(784, 300)
(300,)
print(weights)
output:
[[-0.0189958 0.04178642 -0.02973261 ... 0.02392814 -0.06515371
-0.01094473][-0.06765752 0.05939554 0.05841401 ... -0.03066007 0.07289664
0.03665416]
[ 0.02429469 0.06326848 0.03675973 ... -0.06022335 0.03570367
0.04572336]
...
[ 0.03738911 0.0086766 -0.05104835 ... 0.03970996 -0.00280691
-0.06960735][ 0.06139068 0.00252346 -0.00775075 ... 0.05078986 -0.03957617
0.0160888 ]
[ 0.06548205 0.01272491 -0.05994889 ... -0.05388444 0.06571051
-0.02650245]]
print(biases)
output:
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
### 모델 컴파일(compile)
#- 모델을 구성한 후, 사용할 손실 함수(loss function), 옵티마이저(optimizer)를 지정
model.compile(loss = 'sparse_categorical_crossentropy',
optimizer='sgd',
metrics = ['accuracy'])
sparse_categorical_crossentropy: 클래스가 배타적 방식으로 구분, 즉 (0, 1, 2, ..., 9)와 같은 방식으로 구분되어 있을 때 사용categorical_cross_entropy: 클래스가 원-핫 인코딩 방식으로 되어 있을 때 사용binary_crossentropy: 이진 분류를 수행할 때 사용




교차 엔트로피 오차 식: $ \qquad \qquad E = - \frac{1}{N}\sum{n} \sum{i} y_i\ log\tilde{y}_i $
정답 레이블()은 원-핫 인코딩으로 정답인 인덱스에만 1이고, 나머지는 모두 0이라서 다음과 같이 나타낼 수 있음
$ \qquad \qquad E = - log\tilde{y}_i $
keras.optimizer.SGD(): 기본적인 확률적 경사 하강법keras.optimizer.Adam(): 자주 사용되는 옵티마이저


경사하강법은 한 스텝마다의 미분값에 따라 이동하는 방향을 결정
의 값이 변하지 않을 때까지 반복
즉, 미분값이 0인 지점을 찾는 방법



mae나 accuracy 사용acc로도 사용 가능fit()
x: 학습 데이터y: 학습 데이터 정답 레이블epochs: 학습 회수batch_size: 단일 배치에 있는 학습 데이터의 크기validation_data: 검증을 위한 데이터evaluate()
predict()
오차역전파 알고리즘
오차역전파 학습의 특징
신경망 학습에 있어서 미분가능의 중요성

합성함수의 미분 (연쇄법칙, chain rule)
여러개를 연속으로 사용 가능
$ \quad \frac{\partial f}{\partial x} = \frac{\partial f}{\partial u} \times \frac{\partial u}{\partial m} \times \frac{\partial m}{\partial n} \times \ ... \ \frac{\partial l}{\partial k} \times \frac{\partial k}{\partial g} \times \frac{\partial g}{\partial x}
$
각각에 대해 *편미분 적용가능
편미분 : 다변수 함수에서 하나의 변수를 제외한 나머지 변수들을 상수로 취급하고, 해당 변수에 대해서만 미분을 수행하는 것

오차역전파의 직관적 이해