☭DEVELOPER/#6 인공지능(AI-X) 프로젝트

[AI]모델 서빙/ 안면인식

조반짝 2024. 2. 3. 16:18
728x90
반응형

웹환경에서 인공지능을 만들수 있는 사이트

https://mediapipe-studio.webapps.google.com/home

 

로그인 - Google 계정

이메일 또는 휴대전화

accounts.google.com

 

머신러닝_딥러닝으로 서비스 만들기.pdf
15.25MB


Pytriton

모델 서빙 방법 살펴보기


Model serving pattern

  •  web single pattern

  단순하기 때문에 관리가 쉬움

  문제 발생 시 해결이 더쉬움

  웹 서버, 전처리 및 추론에 python과 같은 하나의 프로그래밍 언어를 사용 가능

  • sychronous pattern

   단순함으로 관리가 쉬움

   트랜잭션 추적, 모니터링 등과 같은 모든 운영 측면이 쉬워짐

   예측이 완료될 때까지 프로세스가 진행되지 않으므로 서비스 워크플로우가 단순해짐

   파이썬으로 하면 유지보수 측면에서 부족하기 때문에 자바로 사용도 가능 

   예측 지연시간은 성능 병목 현상이 될수 있음,

   예측 지연시간으로 인해 사용자 경험이 저하되지 않도록 해결방법을 고려해야함

  • preprocess-prediction pattern

   장애 격리를 통해 리소스를 효율적으로 사용할 수있음

   리소스 사용 및 확정성의 유연성

   각 구성 요소에 대한 라이브러리 및 언어 버전을 선택할 수 있음

   리소스가 복잡해져 운영 비용이 증가

   전처리와 예측사이의 네트워크 연결이 병목 현상이 발생할 수 있음

   동시요청 시 파이프라인 효과로 균등하게 시간을 처리함

   전처리와 모델처리를 구분하는데 처리

   음성처리에 사용

  • microservice vertical pattern

    여러 예측을 순서대로 실행할 수 있음

    이전 예측에 따라 다음 예측서비스를 선택할 수 있음

    순차적으로 해서 예외가 있으면 리턴처리

  •    microservice horizontal pattern

   리소스 사용량을 독립적으로 조정하고 장애를 격리할 수 있음

   다른 모델에 종속되지않고 모델과 시스템을 개발할 수있음

   앙상블로 처리

   시스템이 복잡해질 수 이음

   동기식 사용 사례의 경우 가장느린 추론이 병목현상이 발생함


Model Serving with python web framework

model > deeplearning framework > fast API > Uvicorn > Gunicorn > Docker Pyrhon Image

 

**gunicorn은 실질적으로 쓰면안된다.

비동기식 특성이 있기 때문에 일단 다 접수 비어있으면 무조건 채움, 로드 밸런서 역할이 없다.

그래서 느려진다  

 

기술 면접을 위해 cs, 네트워크 지식을 배워야한다. 

- 라운드로빈은 언제 쓰는가?? 

Concurrent Model Execution

cpu의 스레드

동시에 사용가능

GPU의 스트림

움직여야하는 스트림(줄기)만들어 주어야함

스트림을 늘려주면 동시추론이 가능하다

> 쉽게하는 방법 파이썬의 멀티프로세스를 사용함 or 도커 컨테이너를 여러개 띄움

Dynamic Batching

GPU메모리가 많기 떄문에 입력데이터를 한번에 처리 , 동시 일괄처리 > BATCH 처리

웹에서는 처리하기 어렵기 때문에

Dynamic Batiching 을 사용하면 최초유저에게는 조금 느려 질 수 있지만 대기했다가 동시 일괄 처리해준다.

 


가상환경 만들기

 

https://pytorch.org/

 

PyTorch

 

pytorch.org

가상환경 프레임워크 > 추론에 사용

<윈도우>

<mac>

vscode 들어가서 proj1 가상환경 실행

 

fast API 설치

uvicorn 설치

 

 

main.py 생성

 

fastAPI예제 > main.py에 붙여넣기

 

정상적으로 실행되는 지 확인!

 

main.py

###########################################################
# STEP 1: Import the necessary modules.
import mediapipe as mp
from mediapipe.tasks import python
from mediapipe.tasks.python.components import processors
from mediapipe.tasks.python import vision

# STEP 2: Create an ImageClassifier object.
base_options = python.BaseOptions(model_asset_path='models/classification/efficientnet_lite0.tflite')
options = vision.ImageClassifierOptions(
    base_options=base_options, max_results=5)
classifier = vision.ImageClassifier.create_from_options(options)

############################################################

from typing import Union
from fastapi import FastAPI

app = FastAPI()


@app.get("/")
def read_root():
    # STEP 3: Load the input image.
  image = mp.Image.create_from_file("cat.jpg")

  # STEP 4: Classify the input image.
  classification_result = classifier.classify(image)

  # STEP 5: Process the classification result. In this case, visualize it.
  top_category = classification_result.classifications[0].categories[4]
  print(f"{top_category.category_name} ({top_category.score:.2f})")
  return {"Hello": "World"}


@app.get("/items/{item_id}")
def read_item(item_id: int, q: Union[str, None] = None):
    return {"item_id": item_id, "q": q}

 

미디어 파이프 설치

pip install mediapipe

 

넘파이 라이브러리 설치

pip install numpy

 

form데이터로 클라이언트에서 요청한 사진 넘겨주기

 

fast API > 배우기 > 파일요청

pip install python-multipart

 

클라이언트에서 이미지 올려보기

try out 클릭!

files로 이미지 올리기

files는 용량이 출력

업로드파일은 파일이름이 출력!

업로드파일과 파일즈가 결과가 다르다

파일즈는 바이트로 받으면 os에서 파일을 읽어줌

업로드 파일은 넘어오기 전에 메타정보만 넘어오기 때문에 한번 더 파일을 읽어주어야함

 

파일즈에 스텝3-5를 붙여넣기

서버 에러가 뜸

 

  • 파일의 전송원리

브라우저에서 이미지를 올렸다.

만약 500kb의 이미지를 보내면 

byte는 바이너리를 담을 수 있는 자료형: 이미 데이터가 넘어온것 500kb가 넘어왔다.

업로드 파일에는 메타정보만 있기 때문에 0kb이다. 업로드파일을 한번더 읽어주어야한다. 

비동기로 await로 걸어주면 비로소 500 kb가 담긴다. > 좀 더 효율적으로 데이터처리가 가능하다.

 

files에서는 에러가 뜨는 이유

 

통신으로 받을 때는 byte 타입으로 받아야한다. 

추론 메소드(mp.Image.create_from_file)를 써서 로컬에 있는 파일을 읽는다.

cat.jpt를 바이트로 바꿀 때 쓰는 함수는 File.open("cat.jpg") 이다 > 굳이 file.open을 할 필요없다. 느려진다.

 

mp.Image.create_from_file 를 byte로 변경하는 3단계

mp.Image.create_from_file 디스크에 있는 파일을 오픈 > 비트맵으로 연산처리 decode() > 미디어파일에서 추론할 수 있는 mp.Image로 변경

 

추가설명

http 파일을 주고받는 통신규약

업로드에서 파일을 쏴줌 > fastapi > files: byte이미지를 코드로 변환, fileupload: 업로드는 메타정보만 날라오고 실질적으로 파일의 내용은 오지않음 > file.read()로 해야 실질적인 파일이 넘어옴

이미지 용량이 클 경우

비동기를 걸어주면서 await를 사용하면 파이프라인을 형성할 수있어서 빠르게 받을 수 있음

 

결론!

content:byte > mp.Image로 바꾸어주어야함 > 바로 추론해서 결과를 내보낼 수 있다.

 

 

미디어파이프에서 mp.Image를 바꿀 수 있음

file에서 업로드할 때 오류가 안 나려면 file:byte > content:byte로 변경해주어야함

미디어파이프 > 솔루션즈 > API

uvicorn main:app 으로 다시 실행 

오류없이 잘 되는것을 볼 수 있다.

######################################################################
# STEP 1: Import the necessary modules.
import mediapipe as mp
from mediapipe.tasks import python
from mediapipe.tasks.python.components import processors
from mediapipe.tasks.python import vision
# STEP 2: Create an ImageClassifier object.
base_options = python.BaseOptions(model_asset_path='models/classification/efficientnet_lite0.tflite')
options = vision.ImageClassifierOptions(
    base_options=base_options, max_results=5)
classifier = vision.ImageClassifier.create_from_options(options)
######################################################################
from typing import Union
from fastapi import FastAPI,File, UploadFile
app = FastAPI()
from PIL import Image
import numpy as np
import io
@app.post("/files/")
async def create_file(file: bytes = File()):
    # STEP 3: Load the input image.
    # image = mp.Image.create_from_file(file)
    # decode image from byte
    pil_img = Image.open(io.BytesIO(file))
    # pil_img = Image.new('RGB', (60, 30), color = 'red')
    # create mp Image
    image = mp.Image(
        image_format=mp.ImageFormat.SRGB, data=np.asarray(pil_img))
    # STEP 4: Classify the input image.
    classification_result = classifier.classify(image)
    # STEP 5: Process the classification result. In this case, visualize it.
    top_category = classification_result.classifications[0].categories[4]
    print(f"{top_category.category_name} ({top_category.score:.2f})")
    return {"Hello": f"{top_category.category_name} ({top_category.score:.2f})"}
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
    return {"filename": file.filename}
@app.get("/")
def read_root():
    # STEP 3: Load the input image.
    image = mp.Image.create_from_file("cat.jpg")
    # STEP 4: Classify the input image.
    classification_result = classifier.classify(image)
    # STEP 5: Process the classification result. In this case, visualize it.
    top_category = classification_result.classifications[0].categories[4]
    print(f"{top_category.category_name} ({top_category.score:.2f})")
    return {"Hello": f"{top_category.category_name} ({top_category.score:.2f})"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: Union[str, None] = None):
    return {"item_id": item_id, "q": q}

 

인사이트 페이스 이용해보기

안면인식 프로그램

안면인식 과정 

1. 박스로 얼굴 찾기

    얼라이어먼트(점5개)


https://github.com/deepinsight/insightface

 

GitHub - deepinsight/insightface: State-of-the-art 2D and 3D Face Analysis Project

State-of-the-art 2D and 3D Face Analysis Project. Contribute to deepinsight/insightface development by creating an account on GitHub.

github.com

insightface 설치

 

에러가 뜬다 (mac은 컴파일러가 있기때문에 안뜬다)

out of output 링크 클릭

 

수정 > c++ 선택 후 설치

 

 

가상환경 나가기

https://github.com/JaidedAI/EasyOCR

 

GitHub - JaidedAI/EasyOCR: Ready-to-use OCR with 80+ supported languages and all popular writing scripts including Latin, Chines

Ready-to-use OCR with 80+ supported languages and all popular writing scripts including Latin, Chinese, Arabic, Devanagari, Cyrillic and etc. - GitHub - JaidedAI/EasyOCR: Ready-to-use OCR with 80+ ...

github.com

글자를 찾고 인식하는 프로그램

 

easyocr 설치하기

pip install easyocr

시간이 오래걸리기 때문에 proj2 에서 다시 설치

ocr.py 파일 생성

이미지 찾아서 이미지 변경

 

ocreasy 실행

python ocr.py

한글을 인식한다.

import easyocr
reader = easyocr.Reader(['ko']) # this needs to run only once to load the model into memory
result = reader.readtext('ocr_img1.jpg')
print(result)

 

이미지 주소 복사를 이용해서 바로 사용할 수 있다.

 

main2.py 에 설치

 

pip install fastapi

pip install "uvicorn[standard]"

pip install python-multipart

pip install mediapipe

 

main2.py

uvicorn main2:app 서버 실행

uvicorn main2:app 다시 실행하면 된다.

문자인식 성공!

 

proj1 로 가상환경 다시 연결하고 인사이트페이스 설치

 

face.py 파일 생성

 

메뉴얼 

https://pypi.org/project/insightface/

 

insightface

InsightFace Python Library

pypi.org

import cv2
   import numpy as np
   import insightface
   from insightface.app import FaceAnalysis
   from insightface.data import get_image as ins_get_image

   app = FaceAnalysis(providers=['CUDAExecutionProvider', 'CPUExecutionProvider'])
   app.prepare(ctx_id=0, det_size=(640, 640))
   img = ins_get_image('t1')
   faces = app.get(img)
   rimg = app.draw_on(img, faces)
   cv2.imwrite("./t1_output.jpg", rimg)

 

#STEP 1
import cv2
import numpy as np
import insightface
from insightface.app import FaceAnalysis
from insightface.data import get_image as ins_get_image


#STEP 2
app = FaceAnalysis(providers=['CUDAExecutionProvider', 'CPUExecutionProvider'])
app.prepare(ctx_id=0, det_size=(640, 640))

#STEP 3
img = ins_get_image('t1')

#STEP 4
faces = app.get(img)

#STEP 5 사진 저장
rimg = app.draw_on(img, faces)
cv2.imwrite("./t1_output.jpg", rimg)

 

실행하면 에러가 뜬다

오닉스 런타임이라는 추론기를 설치해야함

https://onnxruntime.ai/

 

ONNX Runtime | Home

Cross-platform accelerated machine learning. Built-in optimizations speed up training and inferencing with your existing technology stack.

onnxruntime.ai

GPU 없으면 CPU를 선택해야함

 

#파이썬에 설치

pip install onnxruntime-gpu

GPU 가없으면
pip install onnxruntime-cpu

 

face.py 실행

python face.py 

에러가 뜸

윈도우 - gpu버전에서 설치는 됐지만 쿠다 자체에대한 설치가 안되서 에러가 남

              문제는 다른 환경이 꼬이게 됨 > 윈도우에서 gpu를 사용하기 어려움

             

두번째 오류가 뜸

numpy 버전 충돌로 버전을  낮추어야 해결이됨

 

AttributeError: module 'numpy' has no attribute 'int'

없어진 기능을 사용하기 때문에 에러가 생김

 

numpy 삭제

pip uninstall numpy

 

numpy 낮은 버전으로 재설치

pip install numpy==1.23.0

python face.py 재실행

t1_output.jpg 이미지가 생성 되었다!

 

불필요한 부분 지우기

 

이미지 커스텀해보기

 

원하는 사진 이미지 컴퓨터에 저장

안면인식 성공!

 


main3.py 파일 생성

#STEP 1
import cv2
import numpy as np
import insightface
from insightface.app import FaceAnalysis
# from insightface.data import get_image as ins_get_image > 인사이트페이스에서 제공해주는 이미지

#STEP 2
app = FaceAnalysis(providers=['CPUExecutionProvider'])
app.prepare(ctx_id=0, det_size=(640, 640))

########################################
from typing import Union
from fastapi import FastAPI, File, UploadFile
app = FastAPI()

from PIL import Image
import numpy as np
import io
@app.post("/files/")
async def create_file(file: bytes = File()):

    #STEP 3
    # img = ins_get_image('t1')
    img = cv2.imread("twice.jpg", cv2.IMREAD_COLOR)

    #STEP 4
    faces = app.get(img)

    #STEP 5 사진 저장
    rimg = app.draw_on(img, faces)
    cv2.imwrite("./t2_output.jpg", rimg)

 

바이너리로 변환

step2에서 app > face 모두 변경

step4 에서 print(result[0].bbox)

step5에서 return{"result":result[0].bbox} 변경

 

uvicorn main3:app 실행

 

파일을 두개 받아보기

step3,4 이미지와 result 추가

 

face에서 얼굴유사도를 측정하는 임베드 값을 꺼낼것임

 

uvicorn main3:app 실행

 

result 값이 1에 가까울 수록 닮았다.

 

main3.py

#STEP 1
import cv2
import numpy as np
import insightface
from insightface.app import FaceAnalysis
# from insightface.data import get_image as ins_get_image > 인사이트페이스에서 제공해주는 이미지

#STEP 2
face = FaceAnalysis(providers=['CPUExecutionProvider'])
face.prepare(ctx_id=0, det_size=(640, 640))

########################################
from typing import Union
from fastapi import FastAPI, File, UploadFile
app = FastAPI()

from PIL import Image
import numpy as np
import io
@app.post("/files/")
async def create_file(file: bytes = File(),
                      file2: bytes = File()):

    #STEP 3
    # img = cv2.imread("twice.jpg", cv2.IMREAD_COLOR)
    nparr = np.fromstring(file, np.uint8)
    img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
    
    nparr2 = np.fromstring(file2, np.uint8)
    img2 = cv2.imdecode(nparr2, cv2.IMREAD_COLOR)
    
    # img = ins_get_image('t1')

    #STEP 4
    result = face.get(img)
    result2 = face.get(img2)
    
    #예외처리
    if result == None or result2 == None:
        return {"result":"fail"}
    
    face1 = result[0]
    face2 = result2[0]
    
    emb1 = face1.normed_embedding
    emb2 = face2.normed_embedding
    
    #임베딩 값을 비교하기위해서 np.dot메소드를 써서 sim 값에 대입
    sim = np.dot(emb1, emb2)

    #STEP 5 
    return{"result": float(sim)}

 

예외처리 코드오류 수정 

 #예외처리
    if len(result) == 0 or len(result2) == 0:
        return {"result":"fail"}
    
    face1 = result[0]
    face2 = result2[0]

두 데이터간 유사도 측정

embedding 은 계산 방식이 항상 똑같음

Euclidean 두 점간의 사이 거리를 측정

https://en.wikipedia.org/wiki/Similarity_measure

 

Similarity measure - Wikipedia

From Wikipedia, the free encyclopedia Real-valued function that quantifies similarity between two objects "Similarity matrix" redirects here. For the linear algebra concept, see Matrix similarity. In statistics and related fields, a similarity measure or s

en.wikipedia.org

word-embedding : 문자간 거리를 측정하여 문자 유사도 측정

 

이미지임베디드 - 텍스트 임베디드 

텍스트로 이미지를 검색 가능함 : 예를 들어 텍스트로 풍경 묘사를 하면 풍경이미지로 검색됨

 

main3.py

#STEP 1
import cv2
import numpy as np
import insightface
from insightface.app import FaceAnalysis
# from insightface.data import get_image as ins_get_image > 인사이트페이스에서 제공해주는 이미지

#STEP 2 추론기 할당
face = FaceAnalysis(providers=['CPUExecutionProvider'])
face.prepare(ctx_id=0, det_size=(640, 640))

########################################
from typing import Union
from fastapi import FastAPI, File, UploadFile
app = FastAPI()

from PIL import Image
import numpy as np
import io
@app.post("/files/")
async def create_file(file: bytes = File(),
                      file2: bytes = File()):

    #STEP 3
    # img = cv2.imread("twice.jpg", cv2.IMREAD_COLOR)
    nparr = np.fromstring(file, np.uint8)
    img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
    
    nparr2 = np.fromstring(file2, np.uint8)
    img2 = cv2.imdecode(nparr2, cv2.IMREAD_COLOR)
    
    # img = ins_get_image('t1')

    #STEP 4 추론결과
    result = face.get(img)
    result2 = face.get(img2)
    
    #예외처리
    if len(result) == 0 or len(result2) == 0:
        return {"result":"fail"}
    
    face1 = result[0]
    face2 = result2[0]
    
    #유사도 측정
    emb1 = face1.normed_embedding
    emb2 = face2.normed_embedding
    
    #임베딩 값을 비교하기위해서 np.dot메소드를 써서 sim 값에 대입
    sim = np.dot(emb1, emb2)

    #STEP 5 후처리로 비즈니스로직 처리
    return{"result": float(sim)}

 

 

 


참고 자료

https://if.kakao.com/2022/session/13

 

if(kakao)dev2022

함께 나아가는 더 나은 세상

if.kakao.com

 

728x90
반응형