지난 포스팅에서 파이썬 코드로 올라마를 사용하는 방법을 소개했습니다. 이 경우에는 개발자가 코드에서 프롬프트와 올라마 서버 모두를 관리하였습니다. 이 방법은 테스트를 진행하기는 간편한 접근입니다. 하지만 클라이언트(프롬프트)와 서버(올라마)가 과도하게 결합되어 있다는 단점이 있습니다. 이번 포스팅에서는 도커를 이용해 파이썬 코드와 올라마 서버를 분리하여 사용하는 방법을 소개합니다. 이를 통해 클라이언트와 서버를 독립적으로 관리할 수 있게 됩니다. 도커 컴포즈를 이용해 재사용 가능한 인프라를 구성합니다. 따라서 올라마 서버는 한 클라이언트에서 분리되어, 다른 클라이언트와도 통신할 수 있는 개선된 형태를 가지게 됩니다.
✨ 소개 🦙
로컬 환경에서 파이썬 코드로 별다른 설정 없이 올라마를 사용한다면, 올라마 서버가 로컬에 함께 실행됩니다. 그럼 `11434` 포트의 `http://localhost:11434` 주소로 접근할 수 있습니다. 이 때 '파이썬 코드'는 클라이언트가 됩니다. 그리고 올라마 서버에 대한 제어를 클라이언트가 하는 형태가 되는데요. 그럼 클라이언트 사용을 종료한다면 올라마 서버도 함께 종료됩니다. 이는 올바른 시스템 구성이 아닙니다. 클라이언트와 서버는 독립적으로 제어할 수 있어야 해요. 그럼 분리해야 겠죠?
어떻게 할 수 있을까요? 파이썬 코드 외부에 서버를 실행하면 됩니다. 이 블로그에서는 도커를 이용해서 올라마 서버를 띄워보겠습니다. 편한 설정을 위해 도커 컴포즈(docker-compose)를 사용하겠습니다. 다음 환경에서 올라마를 테스트를 진행하겠습니다.
- 파이썬: python 3.12
- 가상환경 이름: pilot-ollama
- 라이브러리: ollama == 0.3
- 데모 코드
실행 결과는 깃헙 리포지토리의 노트북을 참고해 주세요!
이 블로그에서 실험할 경우는 3가지 입니다
- 일괄 응답: 질문 `인생이란 무엇인가요?`
- 스트리밍 응답: 질문 `인생이란 무엇인가요?`
- 이미지에 대한 응답: 고양이 사진 + 질문 `사진 속 동물은 무엇인가요?`
🧑💻 코드 예제: 일괄 응답
이전 예제들과 마찬가지로 먼저 환경 설정을 하겠습니다.
이전 예제들과 마찬가지로 먼저 환경 설정을 하겠습니다.
pip install -qq --upgrade pip
pip install --upgrade ollama
이 때 로컬에 ollama가 설치될 수 있습니다. 하지만 실행은 하지 않고 도커 컴포즈(docker-compose)로 올라마 서버를 생성합니다.
아래의 YAML을 `docker-compose.yaml` 혹은 `lv2-docker-compose.yaml` 의 이름으로 저장합니다.
services:
ollama:
image: docker.io/ollama/ollama:latest
ports:
- 11434:11434
volumes:
- ollama-data:/root/.ollama
volumes:
ollama-data:
그리고 터미널에서 다음 명령어를 실행합니다.
docker compose -f docker-compose.yaml pull
docker compose -f docker-compose.yaml -p pilot-ollama up -d
그리고 다음 코드를 실행합니다.
import sys, ollama
assert (sys.version_info.major, sys.version_info.minor) == (3, 12)
client = ollama.Client(host='http://localhost:11434')
client.list()
이전 에제와는 다르게 client를 선언합니다. 그리고 도커 컴포트에 명시되어 있는 올라마 서버에 접속합니다.
ollama에서 직접 서버를 실행하는 것과 같은 포트를 사용합니다. 확실히 구분하기 위해서는 11434 대신 다른 <원하는 포트>로 선언하셔도 됩니다. 대신 도커 컴포트에서도 동일하게 "<원하는 포트>:11434"로 맞춰주시면 됩니다.
다음 명령어로 서버에 `gemma2` 모델을 pull 요청합니다. 모델을 처음 내려받을 때 10분 이상 시간이 걸릴 수 있습니다.
client.pull('gemma2')
response = client.chat(
model = 'gemma2',
messages = [
{
'role': 'user',
'content': '인생이란 무엇일까요?',
}
],
stream = False,
)
print(response['message']['content'])
`client.chat()` 함수는 올라마 API를 호출해 메시지를 바로 보냅니다. 내 컴퓨터에서 도커로 실행 중인 올라마 서버는 `localhost:11434` 주소로 서비스 중입니다. 파이썬 코드의 올라마 라이브러리는 이 주소로 API 요청을 보내고 응답을 후속 코드에 전달합니다.
만약 올라마 도커가 실행 중이 아니라면 오류가 날 수 있습니다. 정상적으로 실행 중이라면 도커에서 다음과 같은 로그를 확인할 수 있습니다.
결과는 다음과 같은 마크다운의 형태로 한 번에 출력됩니다.
저는 챗봇이기 때문에 인생의 의미에 대한 명확한 답을 드릴 수 없습니다. 인생의 의미는 개인마다 다르고, 철학적 고찰과 경험을 통해 발견되는 주관적인 질문입니다. 하지만 여러 사람들의 생각과 관점을 바탕으로 몇 가지 아이디어를 제시해 드릴 수 있습니다.
* **성장과 학습**: 인생은 계속해서 배우고 성장하며 세상을 이해하는 여정이라고 할 수 있습니다. 새로운 경험, 관계, 지식을 통해 스스로를 발전시키고 더 풍요롭게 살아갈 수 있다는 의미를 가질 수 있습니다.
* **관계와 연결**: 인간은 사회적 동물이며, 다른 사람들과의 관계에서 의미와 행복을 찾는 경향이 있습니다. 사랑하는 사람들과의 유대감, 우정, 공동체에 대한 소속감 등이 우리에게 삶의 목적과 가치를 부여할 수 있습니다.
* **창조와 기여**: 세상에 새로운 것을 만들고, 다른 사람들에게 도움을 주는 활동은 인생에 의미를 더하는 요소가 될 수 있습니다. 예술, 과학, 기술 등 다양한 분야에서 자신의 능력을 발휘하고 사회에 기여함으로써 삶의 목적과 가치를 찾을 수 있습니다.
* **즐거움과 행복 추구**: 인생은 단순히 고통과 어려움만으로 이루어지는 것이 아니라, 즐거움과 행복을 경험하는 것도 중요합니다. 우리가 좋아하는 활동, 취미, 여행 등을 통해 삶의 의미와 가치를 느낄 수 있습니다.
결국 인생의 의미는 각자 스스로 찾아나가야 할 질문입니다. 다양한 관점과 경험을 통해 자신에게 맞는 답을 찾고, 그에 따라 행복하고 의미 있는 삶을 살아갈 수 있기를 바랍니다.
출력 전체를 후속 코드에 전달해야 한다면 `stream = False`으로 응답을 요청하시면 됩니다.
🧑💻 코드 예제: 스트리밍 응답
만약 응답을 스트리밍의 형태로 받고 싶다면 `stream = True`로 설정한 다음과 같은 코드로 작성하시면 됩니다.
client.pull('gemma2')
stream = client.chat(
model = 'gemma2',
messages = [
{
'role': 'user',
'content': '인생이란 무엇일까요?',
}
],
stream = True,
)
for chunk in stream:
print(chunk['message']['content'], end='', flush=True)
위 코드의 `print(..., end='', ...)` 함수에서 알 수 있듯이 줄 나눔없이 출력을 이어가면서, GPT가 응답하듯이 결과를 보여줄 수 있습니다.
🧑🎨 코드 예제: 이미지에 대한 응답
이미지를 입력하고 이미지에 대한 응답을 받고 싶다면 다음과 같은 코드로 작성하시면 됩니다. 이를 위해 vision 기능을 지원하는 모델을 사용해야 합니다. 최근에 공개된 'llama3.2-vision' 모델을 사용하겠습니다. 이전 블로그와 다르게 이미지를 불러와 base64로 인코딩하여 첨부하였습니다.
컨테이너와 데이터를 직접 공유하지 않기 때문에 이미지 주소가 아닌 이미지 데이터를 요청에 실어서 보내야 합니다.
import base64
client.pull('llama3.2-vision')
image_path = '../assets/tolga-ahmetler-d6lbhI_ekBk-unsplash.jpg'
with open(image_path, 'rb') as image_file:
encoded_string = base64.b64encode(image_file.read()).decode('utf-8')
stream = client.chat(
model='llama3.2-vision',
messages=[
{
'role': 'user',
'content': '이 사진에 나온 동물 대해서 설명해주세요. 답변은 한국어로 해주세요.',
'images': [ encoded_string ], # 이미지 인코딩 문자열
}
],
stream=True,
)
for chunk in stream:
print(chunk['message']['content'], end='', flush=True)
이미지 크기가 크다면 응답에 오랜 시간이 걸릴 수 있습니다. 적절한 크기의 이미지를 선택하시는 게 좋습니다. 위 코드의 응답은 다음과 같습니다.
사진 속 동물은 고양이라는 것이 분명합니다. 고양이는 식육목에 속하는 포유류의 일종으로, 몸길이는 23~30cm, 무게는 3~6kg입니다. 고양이의 등에는 짧고 격자 모양의 털이 많으며, 발가락은 매우 길어 휠체어가 가능합니다.
사진 속 고양이는 주로 야생에서 살고 있는 것으로 보입니다. 사진 속 고양이가 자주 나타나는 환경을 보면 주변에 많은 낙엽이 있고, 나무와 그늘 아래로 이동하고 있기 때문에 야생 고양이라는 것을 짐작할 수 있습니다.
사진 속 고양이는 검은색과 흰색의 얼룩무늬를 띄고 있습니다. 이러한 색상은 주변에 많은 낙엽이 떨어진 환경에서 자연스러운 것이며, 사진 속 고양이의 털 또한 길고 거칠게 보입니다. 이것도 야생 고양이가 아닌 것을 알 수 있는 기준점 중 하나입니다.
사진 속 고양이는 매우 청순하고 아름다운 모습을 드러내고 있습니다. 고양이라는 종은 모든 사람들의 마음속에 반드시 남기는 자리를 차지하는 동물로 알려져 있으며, 인간의 삶에도 많은 도움을 주는 동물이기도 합니다.
여기까지 내 컴퓨터 환경에서 코드와 도커로 올라마를 사용하는 예제를 소개했습니다. 클라이언트와 서버를 분할하여 확장성을 확보한 형태로 변경할 수 있었습니다. 위 코드에 대한 데모는 이 노트북 링크와 이 도커 컴포트 링크를 통해 살펴보실 수 있습니다. 여러분의 LLM 개발에 도움이 되기를 바라며 마치겠습니다. 읽어주셔서 감사합니다.
'LLMOps' 카테고리의 다른 글
🦙 올라마(Ollama)#5: 사설망에서 올라마(Ollama) LLM 사용하기 (0) | 2024.11.23 |
---|---|
🦙 올라마(Ollama)#2: 내 컴퓨터에서 코드로 올라마 사용하기 (12) | 2024.11.16 |
🦙 올라마(Ollama): 나만의 GPT를 서비스 할 수 있는 도구를 소개합니다 (3) | 2024.11.15 |
어제보다 오늘 더 공부 잘하는 코딩냥이. 어제보다 오늘 더 일 잘하는 코딩냥이.
포스팅이 좋았다면, 오류를 발견했다면, 더 좋은 아이디어가 있다면 댓글 부탁드립니다!