CLIP model은 Text로 들어오는 자연어를 Transformer 아키텍처를 사용해서 처리한다. 입력된 Text는 Tokenize되어 Embeding 된 후에 여러 개 층으로 구성된 Transformer 인코더를 통해 처리됩니다. 처리된 자연어는 Vector값으로 반환 되고, 자연어의 의미와 문맥이 어떤 이미지를 의미 하는 지를 512 차원의 Vector 값으로 나타내게 됩니다.
CLIP model process
이제, CLIP을 사용해서 검색에 사용할 자연어를 vector embedding해보자. (이전 포스트에서 이미지를 vector embedding 하던 코드를 조금 변형해서 사용하면 된다.)
from sentence_transformers import SentenceTransformer
from PIL import Image
# 영어 모델 clip-ViT-B-32
# 다국어 모델 clip-ViT-B-32-multilingual-v1
img_model = SentenceTransformer('clip-ViT-B-32-multilingual-v1', device='cpu')
def vectorize(text):
embedding = img_model.encode(text)
return embedding.tolist()
vectorize('빨간 사과')
위 코드를 실행해보면 텍스트가 vector embedding되어 512 차원의 vector를 반환하는 것을 확인 할 수 있다. 반환 받은 vector 값을 이용하여서 이미지 검색을 해보자.
Elasticsearch(이하 ES)에서 자연어나 이미지로 유사한 이미지를 검색하는 방법에 대해서 알아보자.
유사한 이미지를 찾기 위해서는 먼저 이미지들 간의 유사도를 측정할 방법이 필요하고, 유사도를 측정하기 위해서 Vector를 사용할 수 있다. 이미지를 Embedding을 통해서 Vector로 변환하고, Vector 간의 유사도(Cosine similarity)를 계산해서 유사한 이미지를 찾을 수 있다.
그러면, 이미지를 Vector embedding하는 방법은 무엇이 있을까?
이미지를 vector embedding하는 방법과 이를 만들어주는 ML 모델들은 이미 많이 공개 되어 있다. ResNet, VGGNet, Inception 등 많은 모델들이 있다. 모델들의 성능을 잘 비교해서 알맞은 모델을 사용하면 된다.
Azure SQL(이하 sql)의 data를 ElasticSearch(이하 es)로 검색을 하기 위해서 sql 데이터를 es의 index로 옮겨야 한다. 데이터를 옮기는 방법은 여러가지 방법이 있지만 일반적으로 많이 쓰이는 데이터 처리 오픈소스인 Logstash를 사용하여 옮기는 방법을 알아보자.
Logstash를 운영하는 방식도 다양하지만 간단하게 사용하려면 역시 K8S만 한 것이 없기 때문에 K8S에 구성해보자.
Raspbian은 하드웨어 제품인 RPi(Raspberry Pi)와 Linux계열의 OS(Operating System)인 Debian의 합성어로 Raspberry Pi Foundation이 개발한 RPi 전용 OS다.
RPi는 Raspberry Pi Foundation에서 학교와 개발도상국에서 기초 컴퓨터 과학의 교육을 증진시키기 위해 개발한 신용카드 크기의 싱글 보드 컴퓨터이다. 크기가 작고 전력 소비가 5V-2A로 동작하도록 설계되어 있기 때문에 Raspbian은 저전력 ARM CPU에 상당히 최적화되 도록 만들어 졌다.
Raspberry Pi 4 board
Raspbian을 사용하기 위해서는 앞서 설명한 RPi라는 하드웨어가 필요하다.
(Desktop 버전을 사용하여 live Disc를 생성하거나, 가상머신을 이용하여 PC에 설치 할 수도 있다.)
RPi Model B+
RPi 2 Model B
RPi 3 Model B
RPi 4 Model B
SoC
BCM2835
BCM2836
BCM2837
BCM2711
CPU
ARM11 @700MHz
Quad Cortex A7@900MHz
Quad Cortex A53@1.2Ghz
Quad Cortex A72@1.5Ghz
Instruction Set
ARMv6
ARMv7-A
ARMv8-A
ARMv8-A
GPU
250MHz VideoCore IV
250MHz VideoCore IV
400MHz VideoCore IV
500MHz VideoCore VI
RAM
512MB SDRAM
1GB SDRAM
1GB SDRAM
1, 2 or 4 GB
Wireless
None
None
802.11n/Bluetooth 4.0
802.11n/Bluetooth 5.0
Video
HDMI/Composite
HDMI/Composite
HDMI/Composite
2x micro-HDMI/Composite
Audio
HDMI/Headphone
HDMI/Headphone
HDMI/Headphone
HDMI/Headphone
RPi는 그래픽 성능을 띄어나지만 매우 저렴한 가격(약 25~35$)에 구입할 수 있다.
RPi는 OSHW(Open Source Hardware)라서 하드웨어 스팩은 물론이고 전용 OS인 Raspbian역시 오픈소스로 공개 되어 있기 때문에 하드웨어만 구입하면 나머지 사용하는 것에 대해서는 100% 무료로 사용할 수 있다.
RPi 전용 OS인 Rapbian은 Debian을 기반으로 만들어 졌기 때문에 대부분의 주요 명령어는 Debian과 거의 동일하게 사용하는 하다.
APT(Advanced Package Tool)을 통한 소프트웨어 설치 / 업데이트
dpkg(Debian package) 형식의 패키지 소프트웨어 사용
PIXEL(Pi Improved Xwindows Environment, Lightweight)이라는 GUI 기능 제공을 한다. 이를 통해서 데스크탑 환경을 사용할 수 있다. 특히, 데스크탑 환경 중 App Store와 동일한 개념의 PI Store 제공하여 호환되는 Package들을 쉽게 제공 받을 수 있도록 되어 있다.
Raspbian Pi Store
오픈소스 싱글 보드계열 중에서 저렴하고 파워풀한 기능을 제공하고 있어서 가성비가 좋은 제품으로 많은 개발자들에게 사랑 받고 있다.
전 세계적으로 많은 개발자들이 사용하고 있다 보니 많은 관련 개발 커뮤니티들도 분야별로 자연스럽게 형성되고 있다. 초급자라면 관심 있는 분야의 커뮤니티에서 많은 정보(Tip)들을 제공 받을 수 있다. (오픈소스의 장점이 잘 살려진 것 같다.)
데이터 과학을 수행할때 주로 사용되는 언어로는 Python과 R이 있다. 그리고 이 2가지 언어를 지원하는 IDE 환경도 많이 나와 있는데, 그 중 협업 환경에서 많이 선호되는 Jupyterhub 사용에 대해서 알아보겠다.
Jupyterhub은 Project Jupyter라는 비영리 단체에서 개발한 오픈소스 프로젝트다. BSD라이선스를 따르고 있어서 누구나 100% 무료로 사용할 수 있다.
Jupyterhub는 특정 사용자 그룹별로 Jupyter Notebook(이하 Notebook)이라는 가상 개발 환경을 제공한다. 데이터 과학을 수행하는 사용자는 Notebook이라는 가상 개발 환경안에서 업무를 수행하면 된다. 즉, Jupyterhub는 여러 Notebook들을 공유하는 서버인 것이다. 때문에 사용자는 공유 서버를 통해서 자신이 원하는 Notebook 가상 환경 및 리소스를 제공받을 수 있기 때문에 설치 및 유지 관리 작업에 부담을주지 않는다. 또한 특정 사용자 혹은 그룹별로 별도의 가상환경을 구성할 수 있기 때문에 시스템 관리가 용이하다.
Jupyterhub는 2가지 배포본을 제공되고 있는데 첫 번째는 가상머신 환경에 설치하는 배포본이고 두 번째는 Serverless 환경인 Kubernetes에 설치하는 배포본이다.
클라우드 상에서 운용하기에는 Scale Set을 자유롭게 확장 및 유지관리 할 수 있는 Kubernetes(Serverless framework)환경이 좋기 때문에 가상머신 설치방법은 건너띄고 Jupyterhub를 Kubernetes에 설치 및 구성하는 방법알 알아 보겠다.
참고로, 여기에서 사용된 Kubernetes는 Azure에서 제공하는 AKS(Azure Kubernetes Service)를 이용하였다.
Jupyterhub를 Azure Kubernetes에 설치하기
먼저, Jupyterhub를 설치할 AKS 클러스터에 대한 크리덴셜을 가져오고 최근 환경으로 설정한다.
RESOURCENAME = 'Jupyter'
CLUSTERNAME = 'Jupyterhub'
az aks get-credentials --resource-group=$RESOURCENAME --name=$CLUSTERNAME
kubectl config set-cluster $ClusterName
Jupyterhub를 바로 설치하기 전 jupyterhub를 환경을 구성할 내용을 준비해야 한다.
Jupyterhub 사전 준비작업
Kubernetes는 Serverless 환경이기 때문에 작업한 파일을 영구적으로 보존할 스토리지 볼륨이 필요하다. 다음과 같이 Storage Class를 만들어 준다.
소프트웨어를 잘 만들기 위해서 많은 디자인 패턴들이 사용되는데, 그중에서 DI(Dependency Injection, 의존성 주입)에 대한 개념과 .NET MVC에서 많이 사용되는 DI Framework들 중 하나인 Ninject(Open Source Project)에 대해서 알아보겠다.
DI(Dependency Injection) 란?
프로그래밍에서 사용되는 객체들 사이의 의존관계를 소스코드가 아닌 외부 설정파일 등을 통해 정의하게 하는 디자인 패턴이다. 개발자는 각 객체의 의존관계를 일일이 소스코드에 작성할 필요 없이 설정파일에 의존관계가 필요하다는 정보만 된다. 그러면 객체들이 생성될때, 외부로부터 의존관계를 주입 받아 관계가 설정 된다.
DI를 적용하면, 의존관계 설정이 컴파일시 고정 되는 것이 아니라 실행시 설정파일을 통해 이루어져 모듈간의 결합도(Coupling)을 낮출 수 있다. 결합도가 낮아지면, 코드의 재사용성이 높아져서 모듈을 여러 곳에서 수정 없이 사용할 수 있게 되고, 모의 객체를 구성하기 쉬워지기 때문에 단위 테스트에 용이해 진다.
이제, .NET에서 MVC 프로젝트를 만들때 DI를 구현하기 위해 가장 많이 사용하는 Open Source인 Ninject에 대해서 알아보자.
이제 의존성 주입을 위한 IoC 설정과, 의존관계 설정(bind)작업을 모두 하였으니 Controller에서 사용해보자.
public class CommonStoreController : Controller
{
public CommonStoreController(ICommonStore common)
{
this.commonStore = common;
}
private ICommonStore commonStore;
public int GetItemCount(string id)
{
return commonStore.Add(id);
}
}
Design Pattern 중 DI(Dependency Injection, 의존성 주입)이라는 패턴에은 표준 프로그래밍을 할 때 중요한 요소이긴하지만 무조건 사용해야 하는 것은 아니다. 간단한 프로그램이나 객체간의 결합이 명확하여 구분하지 않아도 되는 경우 굳이 프로젝트를 무겁게(?) 만들 필요없다.