[프로그래머스 인공지능 미니 데브코스] 수업 정리 -10- [클라우드를 활용한 머신러닝 모델 Serving API 개발]

3yeong·2022년 8월 3일
1

1. 클라우드 기초

  • Before Cloud Computing
  • Backgrounds of Cloud computing
  • Cloud Computing
  • Features of Cloud Computing
  • 클라우드 컴퓨팅 운용 모델
  • 클라우드 서비스 제공 모델
  • AWS Cloud Computing

클라우드

4차 산업 혁명과 함께 각광을 받아 다양한 산업영역에서 법과 제도적인 규제의 완화와 함께 사용이 되어지고 있음

Before Cloud Computing

과거에는 인터넷 환경에서 서비를 제공하기 위해 서비스 제공자는 서비스 호스팅에 필요한 모든 것을 직접 구축
데이터 센터를 처음 구축할 때 서비스 아키텍처나 자원 예상 사용량 등을 고려해 구축

데이터 센터(물리적공간)
서버, 저장소
네트워크 방화벽, 보안
운영체제, 기타 개발 도구
전기, 온도, 습도 관리
운영/관리 인력

하지만 서버를 직접 구축하고 운영하는 자원과 인력 비용이 크고 운영 상황의 변화에 능동적으로 대응하기가 어려움

Backgrounds of Cloud computing

회사나 조직이 직접 모든 것을 구축하고 운영하지 않도록 도와주는 IDC 등장

  • IDC는 Internet Data Center의 줄임말로 서버 운영에 필요한 공간, 네트워크, 유지 보수 등의 서비스를 제공함
  • IDC 입주자가 직접 서버를 구입해 들어오기도 하지만 불필요한 또는 유휴 자원이 발생하기 때문에 IDC에서 직접 서버를 임대해 주기도 함

서버 임대를 통해 자원을 효율적으로 이용하고 비용을 줄일 수 있었지만 대부분의 IDC의 서버 임대는 계약을 통해 일정 기간 임대를 하는 유연성이 떨어지는 구조

인터넷 사용자가 크게 증가하고 다양한 서비스를 제공하게 되면서 필요한 때에 필요한 만큼 서버를 증성하기 원하는 온디맨드 수요 증가

  • 제 4차 산업혁명 시대에서 IT기술과 인프로난 빠르게 발전하면서 기존의 서버 구축이나 운영 방식으로는 적절한 시간에 필요한 서비스를 사용자에게 제공하기 어려움
  • 개별 서버 자원 사용률은 평균 10~15%였으며 다른 여러 사용자와 공유함으로 사용률을 향상시켜 자원의 효율적인 활용과 이를 통한 비용 절감을 추구하고자 하였음
사용자 접속량이 늘어나 컴퓨팅 수요가 증가할 때는 오토 스케일링이 필요해요
평상시에 사용하지 않는 유휴 자원은 비용에서 빼주세요
필요한 시점에 바로 사용할 수 있게 운영체제나 필요한 소프트웨어는 미리 설치해주세요

Cloud Computing

클라우드라고 부르기도 하며 "인터넷 기반 컴퓨팅의 일종"

  • 언제 어디서나 필요한 만큼의 컴퓨팅 자원을 필요한 시간만큼 인터넷을 통하여 활용할 수 있는 컴퓨팅 방식
  • 2006년 아마존이 클라우드를 통한 저장공간 및 연산 자원 제공 서비스인 S3와 EC2를 개시하면서 본격적인 클라우드 컴퓨팅 시대가 시작

    AWS는 클라우드 컴퓨팅을 클라우드 서비스 플랫폼에서 컴퓨팅 파워, DB 저장공간, 애플리케이션 및 기타 IT자원을 필요에 따라 인터넷을 통해 제공하고 사용한 만큼만 비용을 지불하는 것으로 정의

4차 산업혁명 시대에서 빅데이터의 수집, 저장, 분석을 위한 방대한 컴퓨팅 자원과 인공지능 개발을 위한 고성능 컴퓨터를 스타트업이나 중소기업이 처음부터 모든 것을 별도로 구입하지 않고도 적은 비용으로 빠르게 필요한 IT환경 마련 가능

Features of Cloud Computing

클라우드 컴퓨팅은 속도, 접근성, 확장성, 생산성, 보안 및 안정성, 측정 가능성 등의 장점을 가짐
특히 인공지능 서비스 제공 시에 도커와 같은 가상화 기술을 통해 GPU 활용과 소프트웨어 설치 및 배포 등의 작업에 비용과 시간을 절감

  • 속도 - 주문형 셀프 서비스 : 클라우드 제공자와 별도의 커뮤니케이션 없이 원하는 클라우드 서비스를 바로 이용 가능
  • 접근성 : 인터넷을 통해 사용자의 위치, 시간과 관계없이 어떤 디바이스로도 접근 가능
  • 확장성 : 갑작스러운 이용량 증가나 변하에 신속하고 유연하게 추가 확장이 가능
  • 생산성 : 하드웨어, 소프트웨어 설치에 들어가는 시간과 비용절감으로 핵심업무에 집중
  • 보안, 안정성 : 클라우드 공급자가 전체적으로 보안이나 안정성에 대해 준비
  • 측정가능성 : 분초 단위로 사용자가 클라우드 서비스를 사용한 만큼만 계량 지급

클라우드 컴퓨팅 운용 모델

클라우드 컴퓨팅은 구축 및 배포 유형에 따라 퍼블릭(Public), 프라이빗(Private), 하이브리드(Hybrid)의 세가지 형태로 구분
퍼블릭(Public)

  • 서비스 유지를 위한 모든 인프라와 IT기술을 클라우드에서 사용
  • AWS, GCP, Azure와 같은 외부 클라우드 컴퓨팅 사업자가 IT자원을 소유하고 인터넷을 통해 제공
  • IT관리 인력이나 인프라 구축 비용이 없는 경우에 유용

프라이빗(Private)

  • 고객이 자체 데이터센터에서 직접 클라우드 서비스를 구축하는 형태
  • 내부 계열사나 고객에게만 제공하여 인프라 확충은 쉬우나 IT기술 확보가 어려운 단점이 있음
  • 보안이 좋고 커스터마이제이션이 가능하며 글로벌 클라우드 사업자가 IT기술만 패키지 형태로 판매하기도 함

하이브리드(Hybrid)

  • 고객의 핵심 시스템은 내부에 두면서도 외부의 클라우드를 활용하는 형태
  • IT기술은 클라우드에서 받고 서비스 유지를 위한 인프라는 고객의 것을 혼용
  • 퍼블릭의 경제성과 프라이빗의 보안성을 모두 고려

클라우드 서비스제공 모델

클라우드 서비스 제공 방식에 따라 IaaS, Paas, SaaS의 세가지 형태로 구분

On-Premises(Owning a car)
IaaS(Leasing a car): 인프라적인 측면만 클라우드를 사용
PaaS(Taking a taxi, 목적지만 선택) : 거기에 플랫폼적인 것도 클라우드를 통해 제공 받음
SaaS(Going by bus, 목적지도 정해짐) : 소프트웨어까지도 클라우드에서 제공 받음(Microsoft Office 365)

클라우드 서비스 제공 사업자

AWS, GCP, Azure, NCP 등 다양한 클라우드 벤드딜이 클라우드 서비스를 제공

AWS Cloud Computing

AWS는 인프라와 기초 서비스 뿐만 아니라 사용자 니즈에 맞는 다양한 애플리케이션 서비스를 제공

2. 실습 : AWS & 실습 환경 세팅

Deep Learning AMI (Ubuntu 18.04) Version 50.0 AMI 를 이용하였다.
AWS계정을 만들어주고 새로운 인스턴스를 만들어 준뒤 kdt.pem 파일을 다운 받는다.

  • 강의 따라 하면 됐는데 강의에서 나온 화면과 좀 달라서 어려웠다.


더 많은 AMI 에서 찾기 눌러서 강의 따라하기!

VS Code로 환경테스트 - 인스턴스 연결 초기화initialization

하기 전에 chmod 400 해야하는데 이 부분에서 윈도우는 cmd 창을 통해 바꾼다.

icacls.exe myec2.pem /reset
icacls.exe myec2.pem /grant:r %username%:(R)
icacls.exe myec2.pem /inheritance:r

visual studio code 설치 및 인스턴스에 접속

visual studio code 설치

이미지의 위치에서 Remote - SSH, Remote Development 설치

각자의 코드는 다르다.
ssh -i "kdt.pem" ubuntu@자동 할당된 IP 주소

conda env list //로 확인

3. API to serve ML model

Architecture of API to serve ML model

AWS EC와 Python Flask 기반 모델 학습 및 추론을 요청/응답하는 API 서버 개발

Interface

사용자는 기계와 소프트웨어를 제어하기 위해 인터페이스를 정해진 메뉴얼에 따라 활용하며 원하는 경험을 획득

  • 컴퓨터의 마우스, 키보드와 같이 입력을 위한 인터페이스와 모니터나 프린터와 같이 정보를 받는 출력을 위한 인터페이스가 있음
  • 인터페이스는 상호 합의된 매뉴얼에 따라 적절한 입력을 받아 기대되는 출력을 제공할 수 있어야 함

API란?

Application Programming Interface의 약자로 기계와 기계, 소프트웨어와 소프트웨어 간의 커뮤니케이션을 위한 인터페이스를 의미

  • 노드와 노드 간 데이터를 주고 받기 위한 인터페이스로, 사전에 정해진 정의에 따라 입력이 들어왔을때 적절한 출력을 전달해야함
  • 스마트폰과 스마트 워치의 알림 공유, 컴퓨터를 통한 브라우저 정보 호출

Restful API for ML/DL model inference

REST 아키텍처를 따르는 API로 HTTP URI를 통해 자원을 명시하고 HTTP Method를 통해 필요한 연산을 요청하고 반환하는 API를 지칭

  • RESTful API는 데이터나 정보의 교환/요청 등을 위한 인터페이스를 REST 아키텍처를 따라 구현한 API
  • 일반적으로 데이터 값을 담아 요청하고 모델이 추론한 결과에 대한 return을 json 형태로 보통 반환하도록 설계
  • RESTful API는 요청 메시지만 봐도 어떤 내용으로 되어있는지 알 수 있도록 표현됨
  1. 사기꾼이 부정적인 방법으로 인출을 시도할때 인출을 위한 거래정보가 server로 넘어간다.
  2. sever는 모델이 필요로 하는 정보로 가공해서 인공지능에 넘겨주게 된다.
  3. 인공지능은 이를 분석해서 server에 결과를 제공한다.
  4. server는 이를 바탕으로 인출을 거부하게 된다.

Practical process of machine learning

문제정의, 데이터준비, 모델 학습 및 검증, 모델 배포, 검증, 모니터링 등의 과정을 통해 실제 서비스에 기계학습 모델을 적용

Model Serving

학습된 모델을 REST API 방식으로 배포하기 위해서 학습된 모델의 Serialization과 웹 프레임워크를 통해 배포 준비 필요

  • 모델을 서빙할 때는 학습 시의 데이터 분포나 처리 방법과의 연속성 유지 필요
  • 모델을 배포하는 환경에 따라 다양한 Serving Framework를 고려하여 활용
  1. Model Training : Data preprocessing, Model fitting, Evaluation
  2. Serializing Model : Save trained model
  3. Serving Model : Load trained model, Defineinference, Deployment

! 모든 과정은 연속성이 유지되어야 한다.

Serialization & De-serialization

학습한 모델의 재사용 및 배포를 위해서 저장하고 불러오는 것

  • Serialization을 통해 ML/DL model object를 disk에 기록하여 어디든 전송하고 불러올 수 있는 형태로 변환
  • De-serialization을 통해 Python 혹은 다른 환경에서 model을 불러와 추론/학습에 사용
  • 모델을 배포하는 환경을 고려해 환경에 맞는 올바른 방법으로 Serialization을 해야 De-serialization이 가능
    Serialization & De-serialization

Skeleton of handler to serve model

결과값은 확률적인 분포, 수치적인 값처럼 숫자의 형태로 나오기 때문에 결과값에 대한 후처리 작업을 해주어야 한다.

  • Handler 를 통해 작업을 하는 것이 편하다.

Model serving을 위한 다양한 Frameworks

딥러닝 모델의 안정적인 serving을 위해 TensorFlow serving이나 TorchServe, TensorRT 같은 프레임워크를 사용하는 것이 일반적

  • Flask와 같은 웹프레임워크는 클라이언트로부터의 요청을 처리하기 위해 주로 사용
  • 별도의 모델 추론을 위한 AP 서버를 운용하여 내부 혹은 외부 통신을 통해 예측/추론값 반환
  • 대용량 데이터 배치처리와 딥러닝 모델의 활용이 늘면서 multi node, multi GPU 환경에서의 안정적인 모델 서빙을 위함

4. 실습 : Serialization & De-serialization

# 아나콘다 가상환경 실행
conda activate pytorch_p36 #버전이 다를 수 있음

# template 소스코드 다운로드
git clone https://github.com.sackoh/kdt-ai-aws
cd ./kdt-ai-aws

# 필요한 라이브러리 설치
pip install -r requirements.txt

가상환경 실행

 conda activate pytorch_p39

소스코드 다운로드

git clone https://github.com/sackoh/kdt-ai-aws

추가 패키지 설치

pip install -r requirements.txt

머신러닝 모델 학습

실습 진행을 위해 사전 준비한 코드를 실행하여 모델 학습 및 저장
Visual Studio에서 좌측상단의 탐색기를 실행 한뒤 Open Folder로 kdt-ai-aws를 열어준다.


#터미널 가서
conda activate pytorch_p37

#학습을 실행한다.
python train_ml.py


이미지를 보면 git에서 download 받고 train model을 하고 나온 정확도가 82.15%로 나온다. 그 후 전처리하고 저장하였다.
모델 폴더에 새로운 파일이 생성됐다.

# 모델을 관리할 수 있는, 전송하고 디스크에 쓸 수 있는
def serialization(model, vectorizer):
    import joblib
    os.makedirs('model', exist_ok=True)
    joblib.dump(vectorizer, 'model/ml_vectorizer.pkl')
    logger.info(f'Saved vectorizer to `model/ml_vectorizer.pkl`')
    joblib.dump(model, 'model/ml_model.pkl')
    logger.info(f'Saved model to `model/ml_model.pkl`')

De-serialization

저장된 모델을 불러와 특정 입력 값에 대한 예측 수행
1. 터미널 환경에서 python jupyter notebook을 실행
2. 아래 예제 코드 테스트하여 de-serialization 확인

#De-serialization
import joblib
model = joblib.load('model/ml_model.pkl')
vectorizer = joblib.load('model/ml_vectorizer.pkl')

# Test loaded model and vectorizer
text = '재미있는 영화입니다.'

model_input = vectorizer.transform([text])
print(model_input.asformat('array'))

model_output = model.predict_proba(model_input)
model_output = model_output.argmax(axis=1)
id2label = {0: 'negative' , 1:'positive'}
print(f'sentiment : {id2label[model_output[0]]}')

먼저 joblib과 vectorizer를 확인한다.

(pytorch_p37) ubuntu@:~/kdt-ai-aws$ python
Python 3.7.10 | packaged by conda-forge | (default, Feb 19 2021, 16:07:37) 
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import joblib
# 각각의 위치에서 가져옴
>>> model = joblib.load('model/ml_model.pkl')
>>> model
MultinomialNB()
# vectorizer : 한글을 수치형으로 나타내기위해 음절단위로 쪼개고 음절을 몇 번 반복했는지 카운팅하여 벡터 형태로 나타낸 것
>>> vectorizer = joblib.load('model/ml_vectorizer.pkl')
>>> vectorizer
CountVectorizer(max_features=100000)

# 텍스트를 넣어 분석해보기
>>> text = '재미있는 영화입니다.'
>>> model_input = vectorizer.transform([text])
>>> model_input
# model_input에는 100000개의 값들이 들어있음
<1x100000 sparse matrix of type '<class 'numpy.int64'>'
        with 2 stored elements in Compressed Sparse Row format>
>>> print(model_input.asformat('array'))
[[0 0 0 ... 0 0 0]]
# input을 모델에게 넣어 예측을 수행, model 안에는 다양한 메소드가 있는데 여기선 예측을 가져옴
>>> model_output = model.predict_proba(model_input)
# 모델은 확률분포의 형태로 나타남
>>> model_output
array([[0.02610797, 0.97389203]])
# 텍스트 문장이 긍정인지 부정인지 확인하기 위해 분석 결과를 후처리 작업함.
>>> model_output = model_output.argmax(axis=1)
>>> model_output
array([1])
>>> id2label = {0: 'negative' , 1:'positive'}
>>> print(f'sentiment : {id2label[model_output[0]]}')
sentiment : positive

serialization과 De-serialization 방법은 동일해야 한다

joblib으로 serialization을 하고 pickle로 불러올 수는 없다.

5. 실습 : inference를 위한 model handler 개발

  • Define inference

skeleton of model handler to serve model

handle()

  • 요청 정보를 받아 적절한 응답을 반환
  1. 정의된 양식으로 데이터가 입력됐는지 확인
  2. 입력 값에 대한 전처리 및 모델에 입력하기 위한 형태로 변환
  3. 모델 추론
  4. 모델 반환값의 후처리 작업
  5. 결과 반환
#감성을 표현하는 API
class ModelHandler:
    def __init__(self):
        self.id2label = {0: 'negative', 1: 'positive'}

    def _clean_text(self, text):
        model_input = []
        if isinstance(text, str):
            cleaned_text = clean_text(text)
            model_input.append(cleaned_text)
        elif isinstance(text, (list, tuple)) and len(text) > 0 and (all(isinstance(t, str) for t in text)):
            cleaned_text = itertools.chain((clean_text(t) for t in text))
            model_input.extend(cleaned_text)
        else:
            model_input.append('')
        return model_input
    def handle(self, data):
        # do above processes
        # 전처리
        model_input = self.preprocess(data)
        # 모델을 불러와 인퍼런스를 통해 아웃풋을 냄
        model_output = self.inference(model_input)
        # 후처리 후 결과 반환
        return self.postprocess(model_output)

initialize()

  • 데이터 처리나 모델, configuation 등 초기화
  1. Configuration 등 초기화
  2. (Optional) 신경망을 구성하고 초기화
  3. 사전 학습한 모델이나 전처리기 불러오기 (De-serialization)

모델은 전역변수로 불러와야한다. 만약 inference를 할 때마다 모델을 불러오도록 한다면 그로 인해 발생하는 시간이나 자원 등의 낭비가 발생한다.
일반적으로 요청을 처리하기 전에 모델을 불러둔다.

def initialize(self, ):
        # De-serializing model and loading vectorizer
        import joblib
        self.model = joblib.load('model/ml_model.pkl')
        self.vectorizer = joblib.load('model/ml_vectorizer.pkl')

preprocess()

  • Raw input을 전처리 및 모델 입력 가능형태로 변환
  1. Raw input 전처리
    (데이터 클린징의 목적과 학습된 모델의 학습 당시 scaling이나 처리 방식과 맞춰주는 것이 필요)
  2. 모델에 입력 가능한 형태로 변환
    (vectorization, converting to id등의 작업)
    def preprocess(self, text):
        # cleansing raw text
        model_input = self._clean_text(text)
        # vectorizing cleaned text
        model_input = self.vectorizer.transform(model_input)
        return model_input

inference()

  • 입력된 값에 대한 예측/추론
  1. 각 모델의 predic 방식으로 예측 확률분포 값 반환
    def inference(self, data):
        # get predictions from model as probabilities
        model_output = self.model.predict_proba(model_input)
        return model_output

postprocess()

  • 모델의 예측값을 response에 맞게 후처리 작업
  1. 예측된 결과에 대한 후처리 작업
  2. 보통 모델이 반환하는 건 확률분포와 같은 값이기 때문에, response에서 받아야 하는 정보로 처리하는 역할을 한다.
    def postprocess(self, model_output):
        # process predictions to predicted label and output format
        # 결과 값 중 가장 높은 확률 값을 뽑아옴
        predicted_probabilities = model_output.max(axis=1)
        # 뽑아온 확률 값이 어떤 id인지, 어떤 label에 해당하는지 뽑기
        predicted_ids = model_output.argmax(axis=1)
        # 뽑아온 값을  0,1 인데 api에서 사람이 이해할 수 있는 값으로 변환
        predicted_labels = [self.id2label[id_] for id_ in predicted_ids]
        return predicted_labels, predicted_probabilities

Testing ML model handler

>>> from model import MLModelHandler
>>> ml_handler = MLModelHandler()
>>> ml_handler
<model.MLModelHandler object at 0x7feeb38621d0>
>>> ml_handler.model
MultinomialNB()
>>> text = ['정말 재미있는 영화입니다.','정말 재미가 없습니다.']
>>> result = ml.handler.handle(text)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'ml' is not defined
>>> result = ml_handler.handle(text)
>>> result
(['positive', 'negative'], array([0.98689943, 0.79583852]))

6.Flask 기반 감성분석 API 개발

네이버 영화리뷰 감성분석 개요

네이버 영화리뷰 데이터로 학습한 ML/DL 모델을 활용해 감성분석 API 개발

  • naive beys모델과 딥러닝 모델로 학습한 두 개의 모델을 서빙하여 0은 부정 1은 긍정을 의미
  • 학습에 사용한 데이터는 박은정님이 공개한 NSMC데이터를 사용

감성분석 API 개발 방향

AWS EC2와 Python Flask 기반 모델 학습 및 추론을 요청/응답하는 API 서버 개발

API 정의

key : value 형태의 json포맷으로 요청을 받아 text index별로 key : value로 결과를 저장한 json포맷으로 결과 반환

  • POST방식으로 predict요청
  • do_fast를
    true로 할 경우, 빠른 추론 속도가 가능한 머신러닝 모델로 추론하도록 함
    false의 경우, 추론 속도는 비교적 느리지만 정확도가 높은 딥러닝 모델로 추론하도록 함

Add Deep Learning model handler

사전 학습한 딥러닝 모델을 활용하여 머신러닝 모델 handler와 동일한 입력에 대해 동일한 결과를 반환하는 handler개발
사전 학습한 모델은 Hugging Face에서 제공하는 외부저장소에서 다운로드 받아 불러옴

import torch

    def initialize(self, ):
        from transformers import AutoTokenizer, AutoModelForSequenceClassification
        self.model_name_or_path = 'sackoh/bert-base-multilingual-cased-nsmc'
        self.AutoTokenizer = AutoTokenizer.from_pretrained(self.model_name_or_path)
        self.model = AutoModelForSequenceClassification.from_pretrained(self.model_name_or_path)
        self.model.to('cpu')

    def preprocess(self, text):
        model_input = self._clean_text(text)
        model_input = self.tokenizer(text, return_tensors='pt', padding=True)
        return model_input

    def inference(self, model_input):
        with torch.no_grad():
            model_output = self.model(**model_input)[0].cpu()
            model_output = 1.0 / (1.0 + torch.exp(-model_output))
            model_output = model_output.numpy().astype('float')
        return model_output

#ML과 동일
    def postprocess(self, model_output):
        # process predictions to predicted label and output format
        predicted_probabilities = model_output.max(axis=1)기
        predicted_ids = model_output.argmax(axis=1)
        predicted_labels = [self.id2label[id_] for id_ in predicted_ids]
        return predicted_labels, predicted_probabilities

    def handle(self, data):
        # do above processes
        model_input = self.preprocess(data)
        model_output = self.inference(model_input)
        return self.postprocess(model_output)

Unittest model handlers

개발한 model handler가 원했던 대로 동작하는지 unittest

python -m unittest -v test_model_handler.py

Flask API 개발 & 배포

model을 전역변수로 불러오고 요청된 텍스트에 대해 예측 결과를 반환하는 코드 입력

배포하기 위해

#결과로 Your service running on port 5000이라는 메시지를 받게 된다.
python app.py

Test API on remote

원격에서 서버로 API에 요청하여 테스트 수행

  • host : EC2 인스턴스 생성 시에 받은 퍼블릭 IP 주소
  • port : EC2 인스턴스 생성 시에 설정했던 port 번호(5000)
  1. 터미널에서

  2. python

과제

Train API 추가
Serialization 실습에서 실행했던 ‘train_ml.py’ 참조하여 http://host:port/train 으로 모델 학습을 요청하는 API 추가

  1. 클라이언트가 POST 또는 GET 요청
  2. Model Training에서 다음 수행
from time import time
from train_ml import *
start_time = time.now()
for mode in ['train', 'test']:
 download_data(mode)
model, vectorizer = train_and_evaluate()
serialization(model, vectorizer)
response = time.now() - start_time
  1. 학습 수행 시간 반환 : 3.69sec
profile
초보 컴공

0개의 댓글