Share
Sign In
기술이야기
머신러닝(Machine Learning) 입문: 타이타닉 생존자 예측 문제로 배우는 ML 기초
김재우
👍
Created by
  • 김재우
Created at
머신러닝 소개
머신러닝, ML 엔지니어가 하는 일
머신러닝(Machine Learning)은 인공지능(AI)의 한 분야로, 컴퓨터가 데이터로부터 스스로 학습하여 패턴을 인식하고 예측을 수행할 수 있게 해주는 기술입니다. 전통적인 프로그래밍 방식은 규칙을 직접 코딩하지만, 머신러닝은 데이터로부터 규칙을 스스로 학습합니다. 이를 통해 복잡한 패턴 인식, 예측, 의사결정 등이 가능해집니다. 메일에서 스팸 메일을 찾아내는 간단한 일에서부터, 의료 분야에서는 질병 예측과 진단, 금융 분야에서는 사기 탐지, 그리고 자율주행 자동차 같은 것들이 머신러닝을 통해 이루어집니다.
머신러닝은 크게 정답이 포함된 데이타로 학습하는 지도학습, 데이타의 패턴을 스스로 찾아내는 비지도학습, 환경과 상호작용하며 보상을 최대화 하는 강화학습. 이렇게 3가지 유형으로 나눌 수 있습니다.
머신러닝 모델은 이런 학습과정을 거쳐 만들어진 규칙입니다. 입력 데이터를 받아 출력을 생성하는 함수와 같은 것입니다. 예를 들어, 스펨 메일 필터링 모델은 입력 메일의 특징을 분석하여 스펨 메일인지 아닌지를 예측하는 알고리즘입니다
머신러닝 모델을 개발하고 배포하는 ML 엔지니어는 데이터를 수집하고 분석하며, 머신러닝 모델을 만들고, 모델을 배포하여 실제 업무에 적용합니다. 데이타의 양이 커서, 데이타에 노이즈나 결측치가 있어서 어려움을 격기도 하고, A하드웨어에서 만든 모델이 B하드웨어에서 제대로 작동안하는 문제를 해결하기도 합니다.
이 글에서는 ML 엔지니어의 기본 업무인 데이터 분석과 모델 구축 과정을 타이타닉 생존자 예측 문제를 예로 들어 설명하겠습니다. 이를 통해 ML 엔지니어의 실제 업무와 그 과정에서 겪는 다양한 도전 과제들을 이해할 수 있을 것입니다.
타이타닉 문제로 시작하기
타이타닉 문제는 1912년 타이타닉 호 침몰 사고 당시의 실제 데이터를 기반으로 승객의 나이, 성별, 탑승 티켓 등급 등의 데이타로 해당 승객이 생존했는지 여부를 예측하는 것입니다.
데이터 규모가 크지 않아(12개의 컬럼, 891건의 데이터) 초보자도 다루기 쉬우며, 이진 분류(생존/비생존) 문제라 평가가 용이합니다. 또한 숫자, 범주형 등 다양한 유형의 데이터와 결측치(누락된 데이타)를 포함하고 있어, 데이터 전처리 과정을 연습할 수 있습니다. 이 문제는 기계학습 파이프라인의 전체 과정을 경험할 수 있는 대표적인 예제입니다.
일단 손으로 풀어보고, 그 다음 실제 머신러닝 모델을 만들어보도록 하겠습니다.
손(Excel)으로 하는 머신러닝
타이타닉 문제의 데이터 파일은 kaggle 에서 다운로드 받으실 수 있습니다. 회원 가입 후 파일을 다운로드 하시면 되고,
train.csv 모델 훈련용으로 사용하는 데이터 파일입니다.
test.csv 모델의 검증용으로 사용하는 데이터 파일입니다.
gender_submission.csv kaggle 에 업로드 해 만들어진 모델을 평가하기 위한 파일입니다. 모델 구축 후 모델의 결과를 이 파일에 저장하고 업로드하면 만들어진 모델의 성능을 확인할 수 있습니다.
데이터 살펴보기
파일을 엑셀에서 열어보면 위와 같은 데이터가 있고, 각 컬럼의 의미는 아래와 같습니다.
PassengerId: 승객 ID
Pclass: 티켓 클래스
Name: 이름
Sex: 성별
Age: 나이
SibSp: 형제/배우자 수
Parch: 부모/자녀 수
Ticket: 티켓 번호
Fare: 요금
Cabin: 선실 번호
Embarked: 탑승 항구
Survived: 생존 여부 (목표 변수)
모델 만들기(가설, 검증)
승객의 생존 여부에 영향을 미치는 주요 요인을 파악하기 위해서는 데이터를 직접 탐색하고 분석해볼 필요가 있습니다. 단순히 눈으로 보기만 해서는 변수들 간의 복잡한 상호작용을 발견하기 어렵기 때문입니다.
그래서 먼저 사전 지식과 경험에 기반하여 생존율과 관련된 가설을 세워봅니다. 예를 들어:
"여성 승객의 생존율이 높을 것이다."
"나이가 어린 승객의 생존율이 높을 것이다."
"상위 등급 티켓을 가진 승객의 생존율이 높을 것이다."
이렇게 수립한 가설들을 실제 데이터에 적용하여 검증해봅니다. 데이터를 적절히 전처리하고 시각화한 후, 가설과 관련된 변수들의 분포와 생존율과의 관계를 살펴봅니다. 이를 통해 가설의 타당성을 판단할 수 있습니다.
성별에 따른 생존율
타이타닉 영화를 봤던 기억을 떠올려보면, 남성보다는 여성을 먼저 구명정에 태웠던 것 같습니다. 성별로 몇 명의 승객이 탑승했고, 생존자 수가 어떻게 되는지 그래프로 확인해보겠습니다.
이처럼 여성의 생존율이 압도적으로 높은 것을 볼 수 있습니다. 아주 간단하게, 여성이면 생존, 남성이면 비생존이라고 판단하는 모델을 만들 수 있을 것 같습니다. 이 모델을 엑셀로 구현하려면, test.csv 시트를 열고 model 1이라는 컬럼을 추가한 후, L컬럼에 아래 함수를 입력하면 됩니다.
=IF(D2:D="female", 1, 0)
별도의 시트를 하나 더 만들고, PassengerId 와 Model 1의 ' 값'만 복사해서 붙여넣고, 컬럼이름을 Survived 로 변경합니다.
그 다음 이 시트를 csv 형식으로 다운로드 받아서 케글의 Submission 페이지에서 업로드를 하면 아래처럼, 이 모델은 76%의 확률로 생존자를 예측하는 것을 확인 할 수 있습니다.
어린이
남성은 모두 비생존으로 보는 것보다는 어린 아이일 경우 생존했을 가능성이 높을 것 같습니다. 이 가설을 한 번 검증해보겠습니다.
=IF(E2="female", -1, IF(AND(E2="male", F2<=12), 1, 0)) 함수를 사용하여 여성, 남성(성인), 남성(어린이)를 구분해 생존율 그래프를 그려봤습니다.
남성의 경우 어린이와 성인 간의 생존율 차이가 크게 보이지 않는 것 같습니다.
1등실 어린이
남성의 생존율은 나이에 따라 크게 차이가 나지 않지만, 1등실 승객이라는 조건을 추가하면 생존율이 더 높아질지 확인해보겠습니다.
=IF(E2="male", IF(AND(B2=1, F2<=20), 1, 0), -1) 이 함수를 적용한 후, 차트를 보면 거의 대부분의 1등급 20세 이하 남성이 생존했음을 알 수 있습니다. 이 로직을 모델에 추가하여 케글에서 모델의 성능을 확인해보겠습니다.
=IF(OR(D15="female", AND(B15=1, E15<=20, D15="male")), 1, 0)
test.csv에 위 함수를 적용한 후 케글에 업로드해보면, 안타깝게도 모델의 성능이 그대로입니다. 그 이유는 test.csv에 나이 값이 비어 있는 행이 있으며, 이 값이 엑셀에서는 0세로 처리되어 모델의 전체적인 성능에 영향을 주지 못하는 것 같습니다.
모델의 성능은 이렇게 비어있는 값(결측치)을 어떻게 처리하느냐도 중요합니다. 결측치 처리에 대해선 나중에 조금 더 설명하겠습니다.
Decision Tree
지금까지 진행한 과정은 Decision Tree 알고리즘을 손으로 구현해 본 것이라 할 수 있습니다.
1.
성별이 가장 중요한 특성임을 발견하고, 이를 기준으로 데이터를 분할했습니다.
2.
남성 데이터에서 나이와 티켓 등급이 중요한 특성임을 발견하고 다시 분할했습니다.
3.
이렇게 반복적으로 데이터를 분할하며 규칙 기반 모델을 만들었습니다.
이렇게 특성의 중요도를 파악하고 데이터를 계층적으로 분할하는 방식이 결정 트리 알고리즘의 핵심 원리입니다.
코드로 풀어보기(python, scikit-learn)
ML은 주로 Python 언어를 사용합니다. 이러한 문제를 푸는 데 주로 사용되는 라이브러리와 프레임워크, 툴들은 다음과 같습니다.
pandas: 데이터 조작 및 분석을 위한 라이브러리로, 데이터 프레임 형태로 데이터를 다룰 수 있습니다.
scikit-learn: 전통적인 머신러닝 알고리즘을 제공하는 Python 라이브러리입니다. 분류, 회귀, 클러스터링 등 다양한 알고리즘과 평가 지표, 데이터 전처리 기능을 포함하고 있습니다.
TensorFlow: 딥러닝 프레임워크로, 복잡한 신경망 모델을 구현하고 학습할 수 있습니다.
Jupyter Notebook: 웹 기반의 대화형 컴퓨팅 환경(REPL)입니다. 코드를 작성하고 실행 결과를 동시에 확인할 수 있어 데이터 분석 및 머신러닝 모델 개발에 널리 사용됩니다.
구현
손으로 풀어봤던 코드로 구현하면 아래와 같습니다. Kaggle의 데이터는 Kaggle에 업로드 해야지만 모델의 성능을 판별할 수 있어, 모든 탑승객의 생존여부가 있는 titanic.csv 파일을 이용했습니다.
import pandas as pd from sklearn.tree import DecisionTreeClassifier from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score # 데이터 로드 data = pd.read_csv('titanic.csv') data.head() # PassengerId Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked # 0 1 0 3 Braund, Mr. Owen Harris male 22.0 1 0 A/5 21171 7.2500 NaN S # 1 2 1 1 Cumings, Mrs. John Bradley (Florence Briggs Th... female 38.0 1 0 PC 17599 71.2833 C85 C # 2 3 1 3 Heikkinen, Miss. Laina female 26.0 0 0 STON/O2. 3101282 7.9250 NaN S # 3 4 1 1 Futrelle, Mrs. Jacques Heath (Lily May Peel) female 35.0 1 0 113803 53.1000 C123 S # 4 5 0 3 Allen, Mr. William Henry male 35.0 0 0 373450 8.0500 NaN S # 이름, 티켓, 객실은 생존 여부에 영향을 미치지 않을 것이라 판단하여 삭제 data = data.drop(['PassengerId', 'Name', 'Ticket', 'Cabin'], axis=1) # null 값 확인 data.isnull().any() # Survived False # Pclass False # Sex False # Age True # SibSp False # Parch False # Fare False # Embarked True # dtype: bool # null 값 처리. Age는 평균값으로, Embarked는 최빈값으로 대체 data.fillna({'Age': data['Age'].mean()}, inplace=True) data.fillna({'Fare': data['Fare'].mode()[0]}, inplace=True) # 성별은 0, 1로 변환 sex_mapping = {'male': 0, 'female': 1} data['Sex'] = data['Sex'].map(sex_mapping) # 탑승지역은 One-hot encoding Embarked -> Embarked_C, Embarked_Q, Embarked_S embarked_dummies = pd.get_dummies(data['Embarked'], prefix='Embarked') data = pd.concat([data, embarked_dummies], axis=1) # 가족 수를 나타내는 FamilySize 특성 추가 data["FamilySize"] = data["Parch"] + data["SibSp"] + 1 # 필요없는 특성 삭제 data = data.drop(["Embarked", "Parch", "SibSp"], axis=1) # 특성과 타깃 분리 X = data.drop('Survived', axis=1) y = data['Survived'] # 데이터 분할 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # 모델 학습 model = DecisionTreeClassifier(max_depth=3, random_state=42) model.fit(X_train, y_train) # 모델 평가 y_pred = model.predict(X_test) accuracy = accuracy_score(y_test, y_pred) print(f"Accuracy: {accuracy:.2f}") # Accuracy: 0.80
코드의 중요한 부분만 설명을 드리면
pandas: CSV파일을 읽어서 DataFrame형식으로 로딩합니다. 결측치 처리, 범주형 데이터 인코딩 등의 전처리 작업을 수행했습니다.
train_test_split: 데이터를 훈련용 데이터와 검증용 데이터로 분리하는 함수입니다. 훈련용 데이터로 모델을 훈련하고, 검증용 데이터로 모델을 평가합니다.
sklearn.DecisinTreeClasifier: scikit-learn의 DecisionTreeClassifier를 사용하여 의사결정 트리 모델을 생성하고, 트리의 깊이를 3으로 제한했습니다.
fit: 훈련데이타로 모델을 학습시키고, predict: 테스트 데이타로 모델을 테스트 해 봅니다.
간단한 코드 몇 줄로도 80%의 정확도를 가지는 모델을 만들 수 있습니다.
실제 DecisionTree 알고리즘을 직접 구현한다면 해당 알고리즘을 이해하고, 구현하고, 테스트하는 데 많은 시간과 노력이 필요할 것입니다. 하지만 ML 알고리즘이 이미 구현된 라이브러리를 활용하면 간단한 코드로 머신러닝 모델을 쉽게 만들어볼 수 있습니다. 이를 통해 알고리즘 구현에 드는 시간과 노력을 줄이고, 데이터 전처리, 모델 학습, 평가 등 end-to-end 머신러닝 파이프라인에 집중할 수 있습니다.
시각화
만들어진 Decision Tree 모델을 시각화하면 각 노드가 어떤 기준으로 데이터를 분할했는지, 최종 예측 결과가 어떻게 도출되었는지를 명확히 확인할 수 있습니다. 이를 통해 우리가 손으로 데이터를 분석해서 나온 결과와 모델이 학습한 결과가 얼마나 유사한지 비교해볼 수 있습니다.
jupyter notebook 에서 위의 코드로 모델을 만들고 아래의 코드로 만들어진 모델을 시각화 해 볼 수 있습니다.
from sklearn import tree import matplotlib.pyplot as plt import pydotplus from IPython.display import Image, display dot_data = tree.export_graphviz(model, out_file=None, filled=True, rounded=True, feature_names=X.columns, class_names=True) graph = pydotplus.graph_from_dot_data(dot_data) display(Image(graph.create_png()))
손으로 확인한 것과 비슷하게, 모델은 일단 성별로 나누고, 그 이후 나이나 Pclass 값으로 생존여부를 판단한 것을 보여줍니다.
모델의 성능을 높이려면
모델의 성능을 높이기 위해서는 다양한 방법을 사용할 수 있습니다. 여기서는 데이터 전처리, 앙상블 기법, 하이퍼파라메타 튜닝에 대해 간단히 소개하겠습니다.
데이터 전처리
데이터 전처리는 모델 성능을 높이는 데 중요한 역할을 합니다. 데이터 품질이 높을수록 모델의 예측 성능도 향상됩니다. 전처리 과정에는 결측치 처리, 이상치 제거, 데이터 정규화, 범주형 변수 인코딩 등이 포함됩니다.
타이타닉 데이터셋에서는 나이와 같은 결측치를 평균값이나 중앙값으로 채우고, 성별과 같은 범주형 변수를 숫자로 변환하는 작업이 필요합니다. 나이의 경우, 이름에 있는 ‘호칭’ Miss, Mrs에 따라 평균값을 구하면 조금 더 정확한 값을 얻을 수 있습니다. 또한 데이터의 스케일링도 중요한데, 서로 다른 범위의 피처 값들을 0-1 사이로 정규화 하면 모델 학습에 도움이 됩니다.
SibSp(형제/배우자 수)와 Parch(부모/자녀 수)를 더해서 family count(함께 탑승한 가족 수)라는 변수를 새로 만든다거나, 혼자 탑승한 사람을 구분하는 필드를 따로 둔다거나 하는 기존의 변수를 조합해 다른 변수를 만들어내는 것도 가능합니다. 이런 기법을 Feature Engineering 이라 합니다.
알고리즘이 알아서 해 줄 수 있는 부분이 많지만, 이 데이터 전처리야 말로 사람의 상상력이 조금 더 중요한 부분이 아닐까 싶습니다.
앙상블 기법
앙상블 기법은 여러 개의 모델을 결합하여 단일 모델보다 더 나은 성능을 내는 방법입니다. 이는 마치 여러 명의 전문가 의견을 종합하는 것과 비슷합니다. 개별 모델의 단점을 보완하고 장점을 강화하여 전체적인 예측 성능을 높일 수 있습니다.
대표적인 앙상블 기법으로는 다음과 같은 것들이 있습니다:
1.
Bagging: 데이터의 부분 집합으로 별도의 모델을 학습시킨 후, 결과를 평균내는 방식입니다. 랜덤 포레스트(Random Forest)가 대표적인 예시입니다.
2.
Boosting: 잘못 예측한 데이터에 가중치를 두어 순차적으로 모델을 학습시키는 방식입니다. 이전 모델의 약점을 보완하며 성능을 높입니다. 그래디언트 부스팅(Gradient Boosting)이 대표적인 예시입니다.
3.
Stacking/Blending: 여러 모델의 예측 결과를 다른 모델의 입력으로 사용하여 최종 예측을 수행하는 방식입니다.
하이퍼파라메타 튜닝
사용자가 직접 설정해야 하는 값들입니다. 예를 들어, 결정 트리의 최대 깊이, 랜덤 포레스트의 트리 개수, 신경망의 학습률 등이 해당됩니다.
하이퍼파라미터 값을 적절히 설정하는 것은 모델 성능에 큰 영향을 미칩니다. 값을 너무 작게 설정하면 모델이 단순해져 훈련 데이터의 패턴을 충분히 학습하지 못하는 과소적합(underfitting) 문제가 발생할 수 있습니다. 반대로 값을 너무 크게 설정하면 모델이 복잡해져 훈련 데이터에 지나치게 맞추게 되어 새로운 데이터에 대한 일반화 능력이 떨어지는 과적합(overfitting) 문제가 발생할 수 있습니다.
Grid Search, Random Search, Bayesian Optimization 등의 자동화된 튜닝 기법을 주로 사용합니다.
이렇게 앙상블 기법과 하이퍼파라미터 튜닝을 활용하면 모델의 성능을 효과적으로 높일 수 있습니다. 실제 데이터에 적용해보고 지속적으로 모니터링하며 개선해나가는 것이 좋습니다.
Scikit learn의 다른 알고리즘들
scikit-learn은 크게 세 가지 종류의 알고리즘을 제공합니다.
분류(Classification): 데이터를 여러 클래스(범주)로 나누는 데 사용됩니다. 예를 들어, 스팸 메일 필터링이나 의료 진단에 활용됩니다.
군집화(Clustering): 레이블이 없는 데이터에서 자연스러운 그룹을 찾는 데 사용됩니다. 고객 분류등에 활용됩니다.
회귀(Regression): 연속형 변수를 예측하는 데 사용됩니다. 주택 가격 예측등에 활용됩니다.
프로젝트의 목적에 맞는 알고리즘을 선택하여 ML 모델을 만들고 활용할 수 있습니다.
딥러닝으로 풀어보면
scikit-learn은 주로 통계적 기법을 이용한 모델을 다룹니다. 통계적 기법만 가지고 해결하기 힘든 문제들을 해결하기 위해 딥러닝(신경망)이 많이 쓰이고 있습니다. 신경망은 뇌의 신경세포를 모델링한 것으로, 최근 하드웨어의 발전으로 신경망의 깊이가 깊은(Deep) 모델을 많이 사용하고 있습니다. 여러 층의 신경망을 이용해 데이터의 복잡한 패턴을 학습하게 됩니다. 음성을 인식하고, 사진을 분류하고, 사진에서 특정 객체를 찾아내는 등의 복잡한 문제를 풀 수 있습니다. ChatGPT도 신경망으로 구현되어 있습니다.
여기서는 TensorFlow를 이용해서 타이타닉 문제를 풀어보겠습니다. TensorFlow는 구글에서 만든 딥러닝 라이브러리로 데이터를 텐서(tensor)라고 하는 다차원 배열의 형식으로 표현하고, 데이터 간의 플로우를 그래프 방식으로 정의합니다. 모델의 구조를 노드(연산)와 엣지(데이터 흐름)로 이루어진 그래프로 정의하고, GPU나 TPU 같은 하드웨어 가속기를 이용하여 그래프를 최적화해 계산을 효율적으로 수행합니다. 그래서 대규모 데이터와 복잡한 모델도 다룰 수 있게 됩니다.
구현
Tensorflow 를 이용하여 타이타닉 문제를 푸는 코드는 아래와 같습니다.
# 데이터 분할 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) import tensorflow as tf from sklearn.preprocessing import StandardScaler from sklearn.metrics import accuracy_score # 모델 정의 model = tf.keras.Sequential([ tf.keras.layers.Dense(64, activation='relu', input_shape=(X_train.shape[1],)), tf.keras.layers.Dense(32, activation='relu'), tf.keras.layers.Dense(1, activation='sigmoid') ]) # 모델 컴파일 model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy']) # 모델 학습 model.fit(X_train, y_train, epochs=50, batch_size=32, validation_split=0.2) # 모델 평가 y_pred = (model.predict(X_test) > 0.5).astype("int32") accuracy = accuracy_score(y_test, y_pred) print(f"TensorFlow Model Accuracy: {accuracy:.2f}") # 18/18 ━━━━━━━━━━━━━━━━━━━━ 0s 691us/step - accuracy: 0.8118 - loss: 0.4067 - val_accuracy: 0.8462 - val_loss: 0.3918 # 6/6 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step # TensorFlow Model Accuracy: 0.83
데이터를 로딩하고 전처리하는 부분은 그대로 두고, train_test_split 이후의 부분만 고쳐서 TensorFlow를 이용한 모델을 만들어 볼 수 있습니다. 3개의 레이어로만 구성된 간단한 모델이지만, Decision Tree를 이용한 모델보다 더 좋은 성능(83% 정확도)의 모델이 만들어진 것을 볼 수 있습니다.
Layer
이 모델은 Sequential 모델을 사용하여 3개의 Dense Layer로 구성된 간단한 신경망을 정의합니다. 레이어(Layer)는 신경망의 기본 구성 요소로, 입력 데이터를 처리하고 변환하여 다음 레이어로 전달합니다.
첫 번째 Dense 층은 입력 데이터의 크기에 맞게 정의되며, 64개의 노드와 ReLU 활성화 함수를 사용합니다. 두 번째 Dense 층은 32개의 노드와 ReLU 활성화 함수를 사용하며, 마지막 Dense 층은 1개의 노드와 시그모이드 활성화 함수를 사용하여 이진 분류를 수행합니다.
Dense Layer: 완전 연결(Fully Connected) 층으로, 각 노드가 이전 층의 모든 노드와 연결됩니다. 이는 입력 데이터의 모든 특징을 고려하여 학습할 수 있게 합니다.
ReLU (Rectified Linear Unit) 활성화 함수: 비선형 활성화 함수로, 입력이 0보다 작으면 0을 출력하고, 0보다 크면 입력 값을 그대로 출력합니다. 이는 신경망이 비선형성을 학습할 수 있게 하여 복잡한 패턴을 인식할 수 있도록 합니다.
Sigmoid 활성화 함수: 출력 값을 0과 1 사이로 압축하는 함수로, 이진 분류 문제에서 주로 사용됩니다. 출력 값이 0.5보다 크면 1로, 작으면 0으로 분류하는 데 사용됩니다.
TensorFlow에는 Dense Layer 외에도 이미지에서 특징을 추출하는 Convolution Layer, 시계열 데이터를 처리하기 위한 Recurrent Layer, 데이터의 분포를 정규화하는 Normalization Layer 등 다양한 레이어를 제공합니다.
더 적절한 사용처
앞서 이야기 했듯 신경망은 전통적인 ML 문제를 푸는데 사용할 수도 있지만, 강력한 패턴 인식 능력, 노이즈에 강한 특징등으로 이미지 인식, 음성인식, 자연어 처리등 비구조적인 데이타를 다룰 때 유용합니다. 최근 하드웨어의 성능이 좋아지는 것과 맞물려 매우 활발한 연구가 진행되고 있어 딥러닝의 사용처는 계속 늘어나고 있습니다.
ML 엔지니어가 되려면?
이 글에서는 데이터 전처리, 모델링, 평가 등 ML 엔지니어의 기본적인 업무 프로세스를 타이타닉 문제를 통해 살펴보았습니다. 하지만 실제 ML 엔지니어가 되기 위해서는 모델 개발 외에도 전체 ML 시스템의 라이프사이클을 관리할 수 있어야 합니다. 이는 문제 정의, 데이터 수집, 전처리, 모델링, 평가, 튜닝, 배포, 모니터링 등의 단계를 포함합니다.
ML 엔지니어에게는 머신러닝 이론과 코딩 능력 외에도 다양한 역량이 필요합니다. 데이터 엔지니어링 기술을 활용해 대규모 데이터를 수집, 저장, 전처리할 수 있어야 합니다. 클라우드 컴퓨팅 기술로 모델을 학습시키고 배포할 수 있어야 합니다. 또한 소프트웨어 엔지니어링 실무 기술인 코드 버전 관리, 테스트, CI/CD, 컨테이너 기술 등을 익혀야 합니다.
Appendix
Ask HN: 머신 러닝 엔지니어들은 업무시간에 어떤 일 하나요? 를 보시면 실제 ML 엔지니어가 업무시간에 어떤 일을 하는지? 어떤 문제를 풀고 있는지? 조금 더 실감나게 이해하실 수 있을 것 같습니다.
어떻게 머신러닝 엔지니어로 피봇할 수 있을까요? 와 같은 글도 ML엔지니어로 피봇하고자 하는 분들에게 도움이 될 것 같습니다.
감사합니다.
Subscribe to '이그나이트'
Welcome to '이그나이트'!
By subscribing to my site, you'll be the first to receive notifications and emails about the latest updates, including new posts.
Join SlashPage and subscribe to '이그나이트'!
Subscribe
👍
전지훈
RestTemplate Request/Response 저장하기
들어가며 개인적인 경험으로, 회사에서 진행하는 프로젝트 중에서 오직 단독 시스템으로 동작하는 서비스는 없었던 것 같다. 대부분은 외부 시스템과 연동하게 되며, MSA를 주력으로 사용하는 부서라면 더더욱 많은 연동을 하게 될 것이다. 연동해야 하는 시스템이 많다는 것은 그만큼 관리 포인트가 늘어나는 것이고, 그에 따라 맞닥뜨려야 할 오류 또한 증가하게 된다. 일부 요청 추적(Request Tracing)을 지원하는 DataDog, PinPoint, Jaeger 등의 서비스를 사용하여 요청/응답을 간략하게 확인할 수 있는 툴을 사용한다면 좋겠지만, 그렇지 않고 독자적으로 시스템에서 외부 시스템을 호출할 때의 로그를 남기고 싶어 하는 부서도 있다. 이 글에서는 그중에서도 RestTemplate을 사용하여 외부 시스템과 연동할 때, 요청/응답에 대해 로그를 DB로 남기는 것을 설명하려 한다. 예제 프로젝트 세팅 이 글에서는 기본적으로 Kafka의 값에는 Json 형식으로 메시지를 저장하는 부분에서 나아가 Json 부분을 암호화하고, 복호화하는 것을 추가하여 설명하려고 한다. 우선 Json 값을 저장하는 예제 프로젝트로 아래와 같이 Producer와 Consumer 세팅하였다. build.gradle DelegateController http://localhost:8080을 호출할 때 RestTemplate을 사용하여, http://httpbin.org/get을 호출하고, 이에 대한 응답을 HttpBinResponse로 정의하여 반환한다. 로컬에서 애플리케이션을 실행시켜 아래와 같이 동작하는 것을 확인할 수 있다. RestTemplate 요청/응답 로그를 DB에 저장하기 1. 사용자 정의 ClientHttpRequestInterceptor 작성하기 Spring에서는 ClientHttpRequestInterceptor를 통해, RestTemplate이 요청/응답을 보내기 전/후의 동작을 정의할 수 있다. 인터페이스는 다음과 같다. HttpRequest: 요청에 대한 객체이며, HttpHeaders getHeaders(), URI getURI(), HttpMethod getMethod()를 가지는 인터페이스다. body: payload에 해당하는 byte의 배열이다. execution: execution.execute(request, body)을 수행해야 실제로 요청을 수행하고, interceptor 메서드에서 반환해야 할 응답 결과(ClientHttpResponse)를 얻을 수 있다. 우선 아래와 같이 LoggingClientHttpRequestInterceptor를 작성하자. 2. 요청 로그 남기기 요청 로그를 남기기 위해서 아래와 같이 LoggingClientHttpRequestInterceptor를 변경하자. HttpRequest.toString()은 오버라이딩 되어있지 않아, 그대로 로그를 남긴다면 인스턴스의 해시 코드 값을 반환하게 된다. 각각 URI, Method, Headers의 getter를 호출하여 원하는 값을 얻을 수 있다. 3. 응답 로그 남기기 응답 로그를 남기기 위해서 다시 아래와 같이 LoggingClientHttpRequestInterceptor를 변경하자. 이번에 위 인터셉터를 적용한 RestTemplate을 호출하면 아래와 같이 정상적인 로그를 확인할 수 있다. 다만 브라우저에서는 비어있는 화면을 확인할 수 있을 것이다.
  • 전지훈
전지훈
Spring Kafka 메시지 암호화
들어가며 개발 업무를 진행하면 종종 개인정보에 대해 다룰 때가 생긴다. 보통 이러한 민감정보는 DB, Redis, Kafka 혹은 로깅에서도 암호화하여 저장하는 것이 권장된다. 이 글에서는 스프링으로 Kafka를 사용할 때, 본문 내용에 대해 암호화하여 저장하는 방법을 다루고자 한다. Json 역/직렬화 예제 이 글에서는 기본적으로 Kafka의 값에는 Json 형식으로 메시지를 저장하는 부분에서 나아가 Json 부분을 암호화하고, 복호화하는 것을 추가하여 설명하려고 한다. 우선 Json 값을 저장하는 예제 프로젝트로 아래와 같이 Producer와 Consumer 세팅하였다. producer와 consumer를 분리하였는데 이는 Spring Kafka에서 제공하는 JsonSerializer/Deserializer의 지원에 대해 설명하기 위해 분리하였다. producer의 코드들 ProducerChatMessage Kafka에 값으로 저장할 내용을 아래와 같이 정의했다. ProducingController Http 요청을 받아 ProducerChatMessage를 Kafka로 전송하는 부분으로 아래와 같이 정의한다. application.yaml Kafka에 대한 설정과 위에서 정의한 ProducerChatMessage를 아래와 같이 Json으로 직렬화하면서 별칭(alias)을 줄 수 있다. spring.kafka.producer.value-serializer와 spring.kafka.consumer.value-deserializer의 값으로 JsonSerializer, JsonDeserializer를 사용할 수 있다. 위 두 개의 클래스를 지정할 때 spring.kafka.[producer|consumer].properties.spring.json.type.mapping으로 위 예제와 같이 콜론(:) 구분으로 별칭을 줄 수 있다. 이러한 별칭은 추후 클래스를 다른 패키지로 변경하거나, 다른 클래스를 사용하게 될 때에도 유연하게 대응할 수 있도록 도와준다. 참고: Spring Boot / Reference / Messaging / Apache Kafka Support 별칭은 Kafka 메시지의 헤더로 저장되며, JsonSerializer가 헤더를 생성, JsonDeserializer가 헤더를 제거하는 식으로 동작한다. ex) key: __TypeId__, value: message 위와 같은 방법으로 아래 예제와 같이, 값에 인터페이스를 받도록 대응할 수 있다. 예제를 단순화하기 위해서 if/else를 사용했지만 실무에서는 사용하지 않기를 바란다. consumer의 코드들 ConsumerChatMessage producer 프로젝트의 ProducerChatMessage에 대응하는 모델을 ConsumerChatMessage로 작성한다. IgniteKakfaConsumer 단순하게 메시지를 받아 로그를 남기는 부분을 consumer로 작성한다.
  • 전지훈
전지훈
Spring Properties를 외부 저장소와 연동하기
들어가며 SaaS(Software As A Service)가 보편화되며 아마 많은 개발자가 이를 구현하기 위해 Twelve-Factor를 읽어보았을 것으로 생각된다. 이 글은 Twelve-Factor에서 설정에 대해 어떻게 Spring Boot를 사용하는 개발자들이 외부의 설정 저장소에 접근하여 설정을 읽을 수 있을지 구현에 관해 이야기하고자 한다. 이미 대부분의 스타트업들은 클라우드 환경에서 시작하고 있으며, 국내 IT 대기업들은 자체 클라우드를 구축하고, IT 업계 외의 사업을 주로 하는 기업들도 클라우드로 옮겨가고 있다. 주된 방향성은 클라우드 환경에 있지만 아직 온프레미스를 주로 사용하는 조직도 있기에, 여기서는 온프레미스와 클라우드 환경에 따라 어떻게 설정을 달리할 수 있는지 다뤄보려고 한다. Spring Externalized Configuration 본문에서 다루고자 하는 모든 개발 방법의 기반은 Spring Documentation에서 이미 기술하고 있는 내용에 대한 응용이다. 또한 외부에서 설정을 읽는 부분에 대한 많은 지원을 이미 Spring Cloud Config에서 제공하고 있으니, 사용하려는 환경이 이미 Spring Cloud Config에서 지원하고 있다면 사용하는 것이 좋다. 이 글은 Spring Cloud Config보다는 좀 더 바퀴를 만드는 방법에 대해 다룬다. 본문이 도움될 만 한 상황 아래와 같은 상황에서 이 글이 도움이 될 것이라 생각된다. Spring Cloud Config를 조금 더 가볍게 모듈화하거나, 자신의 조직에 맞게 구성하고 싶은 경우. 전체 혹은 일부 설정에 대해 Spring Cloud Config에서 지원하지 않는 외부 설정 저장소를 사용하려는 경우. 혹은 아래와 같이 application.properties를 커스터마이징하려는 경우. 외부 환경에서 설정을 개발자가 직접 호출하도록 개발하기 Spring Cloud Config에서 지원하지 않는 환경에 설정을 저장하고, 불러와야 하는 경우가 있다. 사내에서 자체 구축한 시스템을 사용하는 경우이다. 이런 경우 EnvironmentPostProcessor를 진입점으로 간주하고 원하는 결과를 이룰 수 있다. EnvironmentPostProcessor EnvironmentPostProcessor는 ApplicationContext가 초기화되기전에 애플리케이션의 환경 설정을 변경할 수 있으며 아래 인터페이스를 가지고 있다. ConfigurationEnvironment: getPropertySources()를 통해 MutablePropertySources를 사용할 수 있으며, 여기서 property를 추가하거나, 우선순위를 조절하여 덮어쓸 수 있다. 따라서 내부 구현을 통해 외부 설정 저장소에서 설정값을 불러와 우선순위를 높여 PropertySources에 저장하면 외부 설정 저장소의 설정값을 애플리케이션에서 참조할 수 있다. CSV 예제 단순한 예제로 CSV에서 설정을 읽어와 사용하는 경우를 구현해 보자. custom.greeting이라는 설정에 Hello world!를 출력하고자 한다. application.yaml HelloWorldController.java CsvEnvironmentPostProcessor.java
  • 전지훈