케라스 완전 정복
작성자 : 김혜영
다양한 워크플로
케라스 API 설계는 복잡성의 단계적 공개 원칙을 따른다. 시작은 쉽게, 필요할 때 단계마다 점진적으로 학습해 아주 복잡한 경우를 처리할 수 있다. 케라스는 초보자부터 숙련자까지 편하게 사용할 수 있는 API라는 의미이다. 케라스의 모든 워크 플로우는 Layer와 Model같은 API를 기반으로 하기 때문에 한 워크플로우의 구성 요소를 다른 워크플로우에서 사용할 수 있다. 즉, 워크플로우 간에 서로 호출이 가능하다.
케라스 모델을 만드는 여러 방법
케라스에서 모델을 만드는 API는 세 가지이다.
- Sequential 모델: 가장 시작하기 쉬운 API. 기본적으로 하나의 파이썬 리스트이다. 단순히 층을 쌓을 수만 있다.
- 함수형 API: 그래프 같은 모델 구조를 주로 다룬다. 함수형 API는 사용성과 유연성 사이의 적절한 중간 지점에 해당된다. 가장 널리 사용되는 모델 구출 API.
- Model 서브클래싱: 모든 것을 밑바닥부터 직접 만들 수 있는 저수준 방법. 모든 상세한 내용을 와전히 제어하고 싶은 경우에 적합하다. 하지만, 초심자는 다루기 어렵다.
1. Sequential 모델
케라스 모델을 만드는 가장 간단한 방법.
from tensorflow import keras
from tensorflow.keras import layers
model = keras.Sequential([
layers.Dense(64, activation = "relu"),
layers.Dense(10, activation = "softmax")
])
위의 코드로 간단한 Sequential 모델을 정의하였다. 아래는 동일한 모델을 다른 방법으로 정의한 코드이다.
from tensorflow import keras
from tensorflow.keras import layers
model = keras.Sequential()
model.add(layers.Dense(64, activation = "relu"))
model.add(layers.Dense(10, activation = "softmax"))
model.add코드는 파이썬 리스트의 append() 메소드와 비슷하다.
모델의 층은 모델이 처음 호출될 때 만들어진다.(가중치를 만든다.) 층의 가중치 크기가 입력 크기에 따라 달라지기 때문이다. 즉, 입력 크기를 알기 전까지 가중치를 만들 수 없다.
위에서 정의한 model은 어떤 가중치도 가지고 있지 않다. 가중치를 생성할려면 build() 메소드를 호출해야한다.
아래 그림은 build() 호출 없이 model의 가중치를 불렀을 때, 발생하는 에러 화면이다.
가중치를 만들기 위해 다음과 같이 모델을 호출한다.
# model을 build하기
model.build(input_shape=(None,3))
위의 코드는 크기가 (3, )인 입력 샘플 기대한다는 의미이다. 입력 크기의 None은 어떤 배치 크기도 가능하다는 의미이다.
modeld을 build한 뒤에 가중치를 호출하면 다음과 같은 결과를 확인할 수 있다.
build 메소드가 호출되어 가중치가 입력된 뒤엔 summary() 메소드를 사용해 모델의 구조를 출력할 수 있다.
케라스에선 사용자가 모델의 이름과 층의 이름을 지정할 수 있다.
다음은 Sequential 모델의 가중치를 바로 생성하는 방법이다. 모델의 입력 크기를 미리 지정하면 모델의 가중치를 바로 생성할 수 있다. Input 클래스를 사용한다.
model = keras.Sequential()
# Input 클래스를 사용해 입력 크기를 지정한다. shape 매개변수의 값은 같은 배치 크기가 아니라 각 샘플의 크기이다.
model.add(keras.Input(shape = (3,)))
model.add(layers.Dense(64, activation = "relu"))
아래 그림은 위의 코드를 실행했을 때 확인할 수 있는 결과이다.
2. 함수형 API
Sequential 모델은 사용하기 쉽지만 적용할 수 있는 분야가 극히 제한적이다. 하나의 입력과 하나의 출력을 가지며 순서대로 층을 쌓은 모델만 표현 가능하다. 실제론, 이미지와 같은 다중 입력 다중 출력 또는 비선형적인 구조를 모델을 자주 사용한다.
이런 경우, 함수형 API를 사용한 모델을 만든다.
1. sequential과 동일한 모델 정의해보기
위 코드는 Sequential 모델처럼 2개의 층을 쌓은 코드이다. 단계별로 코드를 살펴보자.
- Input 클래스 객체 정의
inputs = keras.Input(shape=(3,), name="my_input")
inputs 객체는 모델이 처리할 데이터의 크기와 dtype에 대한 정보를 가지고 있다.
이런 객체를 심볼릭 텐서(symbolic tensor) 라고 지칭한다. 실제 데이터는 가지고 있지 않지만 사용할 때 모델이 보게 될 데이터 텐서의 사양이 인코딩되어 있다. 즉, 미래의 데이터 텐서를 나타낸다.
- 층을 만들고 입력으로 호출
features = layers.Dense(64, activation ="relu")(inputs)
모든 케라스 층은 실제 데이터 텐서나 심볼릭 텐서로 호출할 수 있다. 심볼릭 텐서로 호출할 경우 크기와 dtype 정보가 업데이트 된 새로운 심볼릭 텐서를 반환한다.
- 입력과 출력을 Model 클래스에 전달
outputs = layers.Dense(10, activation = "softmax")(feature)
model = keras.Model(inputs = inputs, outputs = outputs)
아래 사진은 모델의 summary() 메서드 호출 결과이다.
2. 다중 입력, 다중 출력 모델
고객 이슈 티켓에 우선순위를 지정하고 적절한 부서로 전달하는 시스템을 만들 때, 모델은 3개의 입력을 사용한다.
- 이슈 티켓의 제목(텍스트)
- 이슈 티켓의 텍스트 본문(텍스트)
- 사용자가 추가한 태그(범주형 입력으로 원-핫 인코딩되었다고 가정)
이 모델은 출력이 2개이다.
- 이슈 티켓의 우선순위 점수. 0과 1 사이의 스칼라(Sigmoid 출력)
- 이슈 티켓을 처리해야 할 부서(전체 부서 집합에 대한 Softmax 출력)
위 코드는 아래와 같이 동작한다.
- 모델의 입력 정의
- 입력 특성을 하나의 텐서 features로 연결
- 중간층을 적용해 입력 특성을 더 풍부한 표현으로 재결합
- 모델의 출력을 정의
- 입력과 출력을 지정해 모델을 만든다.
3. 다중 입력, 다중 출력 모델 훈련
Sequential 모델을 훈련하는 것과 거의 동일한 방법으로 모델 훈련 가능. 입력과 출력 데이터의 리스트로 fit() 메소드 호출하면 훈련이 가능하다. 데이터의 리스트는 Model 클래스에 전달한 순서와 같아야 한다.
위 코드는 입력과 타깃 배열 리스트를 전달해 모델을 훈련시키는 코드이다. 입력 순서에 신경 쓰고 싶지 않다면 Input 객체와 출력 층에 부여한 이름을 활용해 데이터를 딕셔너리로 전달가능하다.
4. 함수형 API의 장점: 층 연결 구조 활용하기
함수형 모델은 명시적인 그래프 데이터 구조이다. 층이 어떻게 연결되어 있는지 조사하고 이전 그래프 노드를 새 모델의 일부로 재사용할 수 있다.
방금 정의한 모델의 연결구조(모델의 토폴로지 )를 시각화 해보자.
만약, 모델에 있는 각 층의 입출력 크기를 추가하면 디버깅에 도움이 될 수 있다.
텐서 크기에 None은 배치 크기를 나타낸다. 즉, 이 모델은 어떤 크기의 배치에서도 사용 가능하다.
층 연결 구조를 참조해 그래프에 있는 개별 노드를 조사하고 재사용할 수 있다. model.layers 속성은 모델에 있는 모든 층의 리스트를 가지고 있다. 각 층에 대해서 layer.input과 layer.output을 출력해볼 수 있다.
이를 통해 특성 추출을 수행해 다른 모델에서 중간 특성을 재사용하는 모델을 만들 수 있다. 아래 코드는 중간층의 출력을 재사용하여 새로운 모델을 만들어보는 코드이다.
5. Model 서브 클래싱
서브클래싱은 가장 고급적인 모델 구축 패턴이다.
- init() 메서드에서 모델이 사용할 층을 정의
- call() 메서드에서 앞서 만든 층을 사용해 모델의 정방향 패스를 정의
- 서브클래스의 객체를 만들고 데이터와 함께 호출해 가중치를 만듭니다.
간단하게 이전 예제를 서브클래싱 모델로 다시 만들기
모델을 정의하고 나면 클래스의 객체를 다음과 같이 만들 수 있다.
model = CustomerTicketModel(num_departments=4)
priority, department = model({"title":title_data, "text_body":text_body_data, "tags":tags_data})
지금까지는 layer클래스의 상속과 유사해보인다. 하지만 layer 클래스 상속과 model 클래스 상속은 차이점이 있다. 둘의 차이점은 layer 클래스는 model 클래스와 달리 fit(), evalute(), predict() 메소드를 상속하지 않는다. 간단하게, model 클래스 상속은 모델을 훈련할 때, 필요한 메소드들도 함께 상속할 수 있다.
아래 코드는 model 클래스 상속의 fit(), evaluate(), predict()메소드로 모델을 훈련시키는 코드다.
model.compile(optimizer="rmsprop",
loss=["mean_squared_error","categorical_crossentropy"],
metrics=[["mean_absolute_error"],["accuracy"]])
model.fit({"title":title_data,
"text_body":text_body_data,
"tags":tags_data},
[priority_data, department_data],
epochs=1)
model.evaluate({"title":title_data,
"text_body":text_body_data,
"tags":tags_data},
[priority_data, department_data])
priority_preds, department_preds = model.predict({"title":title_data,
"text_body":text_body_data,
"tags":tags_data})
Model 서브클래싱 워크플로는 모델을 만드는 가장 유연한 방법이다. call() 메소드가 for 루프 안에서 층을 사용하거나 재귀적으로 호출이 가능하다.
서브클래싱된 모델이 지원하지 않는 것이 있다. 서브클랫이 모델에선 모델 로직에 잠재적 오류 가능성을 예측해야한다. 함수형과 서브클래싱 모델은 태생적으로 다르기 때문에 여러 제약사항이 발생한다.
6. 여러 방식 혼합하기
Sequential 모델, 함수형 API, Model 서브클래싱 패턴 중 하나를 선택한다고 다른 패턴을 사용하지 못하는 건 아니다.
- 함수형 모델에서 서브클래싱 층이나 모델을 사용할 수 있다.
- 서브클래싱 층이나 모델의 일부를 함수형 모델로 사용가능
다양한 도구가 있으므로 작업에 적합한 도구를 사용하는 것이 중요하다.
훈련 루프와 평가 루프
1. 내장된 훈련 루프와 평가 루프
케라스는 모델 훈련을 위해 다양한 워크플로를 제공한다. 간단하게 데이터로 fit() 메서드를 호출하거나 처음부터 새로운 훈련 알고리즘을 작성할 수 있는 고급 방법도 제공한다.
기존에 많이 써온 compile(), fit(), evalute(), predict() 워크플로우를 잘 알고 있다.
위의 기존에 써온 워크플로우를 커스터마이징 할 수 있다.
- 사용자 정의 측정 지표를 전달한다.
- fit() 메소드에 콜백(callback)을 전달해 훈련동안 특정 시점에 수행될 행동을 예약함.
1. 사용자 정의 측정 지표
지표는 모델의 성능을 측정하는 열쇠다. 특히, 훈련 데이터 성능과 테스트 데이터 성능 사이의 차이를 측정하는 것이 중요하다. 분류와 회귀에는 일반적으로 keras.metrics 모듈에 포함된 지표를 사용한다. 대부분 이 지표를 사용하지만 일반적이지 않은 작업을 한다면 사용자 정의 측정 지푤르 만들 수 있다.
케라스 지표는 keras.metrics.Metric을 상속받는다.
다음은 Metric 클래스를 상속해 사용자 정의 지표를 구하는 코드다.
2. 콜백 사용하기
대규모 데이터셋에서 model.fit() 메서드를 사용해 수십 번의 에포크를 실행하는 것은 종이 비행기를 날리는 것과 유사하다. 일단, 훈련을 시작하면 경로를 제어할 수 없다는 뜻이다. 케라스 콜백은 model.fit() 호출을 스스로의 상태를 판단해 동적으로 자동 운전하게 만드는 기술이다.
콜백을 사용하는 사례
- 모델 체크포인트 저장: 훈련하는 동안 어떤 지점에서 모델의 현재 가중치를 저장.
- 조기 종료: 검증 손실이 더 이상 향상되지 않을 때 훈련을 중지(훈련동안 얻은 가장 좋은 모델을 저장함)
- 훈련하는 동안 하이퍼파라미터 값을 동적으로 조정: 옵티마이저의 학습률 같은 하이퍼파라미터 조정
- 훈련과 검증 지표를 로그에 기록하거나 모델이 학습한 표현이 업데이트 될 때 마다 시각화
텐서보드를 사용한 모니터링과 시각화
좋은 연구를 하거나 좋은 모델을 개발하려면 실험하는 모델 내부에서 어떤 일이 일어나는지 알아야한다. 케라스는 모델 내부의 일을 텐서보드를 이용해 보여준다.
텐서보드를 사용하면 다음과 같은 일을 수행할 수 있다.
- 훈련하는 동안 측정지표를 시각적으로 모니터링
- 모델 구조를 시각화
- 활성화 출려과 그레디언트의 히스토그램을 그리기
- 임베딩을 3D로 표현
4. 사용자 정의 훈련, 평가 루프 만들기
fit() 워크플로는 쉬운 사용성과 유연성 사이에서 알맞은 균형을 유지한다. 하지만, 사용자 정의 지표, 사용자 정의 손실, 사용자 정의 콜백이 있어도 특수한 작업엔 또 다른 기능을 요구할 수 있다.
무엇보다 fit() 워크플로는 지도학습에 초점이 맞춰져있다. 하지만, 머신러닝엔 비지도학습이나 강화학습과 같은 다양한 학습 방법이 존재한다. 만약에 내장fit() 메소드로 충분하지 않은 상황이라면 사용자가 훈련 로직을 직접작성해야한다.