반응형

파이썬에서 Ollama를 사용해 보자.

 

2026.06.13 - [AI, ML, DL] - [Ollama] Ollama 설치 및 간단한 실행

2026.06.14 - [AI, ML, DL] - [Ollama] Ollama with Python 2

 

import requests
import json

url = "http://localhost:11434/api/generate" # 엔드포인트 URL
# generate는 모델이 텍스트를 생성하는 것을 의미한다.
# 이 엔드포인트는 모델에게 텍스트 생성을 요청하는 역할을 한다.
# 예를 들어, 사용자가 "What is the capital of France?"라는 질문을 보내면, 모델은 이에 대한 답변을 생성하여 반환하게 된다.
payload = {
	"model": "llava",
	"prompt": "What is the capital of France?",
	"stream": False # 스트리밍 여부를 설정하는 옵션으로, False로 설정하면 모델이 생성한 텍스트를 한 번에 반환한다.
			# True로 설정하면 모델이 텍스트를 생성하는 동안 실시간으로 결과를 스트리밍 방식으로 받을 수 있다.
}

headers = { "Content-Type": "application/json" }
response = requests.post(url, data=json.dumps(payload), headers=headers)

if response.status_code == 200:
	print("■ Response:", response.json())
	print()
	print("■ Generated Text:", response.json().get("response", "No generated text found"))
	# response.json().get("response", "No generated text found")는 JSON 응답에서 "response" 키에 해당하는 값을 가져오며,
	# 만약 해당 키가 존재하지 않을 경우 "No generated text found"라는 기본값을 반환한다.
else:
	print("Error:", response.status_code, response.text)

 

한 번에 모든 출력이 완료된다.

 

import requests
import json

url = "http://localhost:11434/api/generate"
payload = {
	"model": "llava",
	"prompt": "What is the capital of France?",
	"stream": True # 스트리밍 여부를 설정하는 옵션으로, False로 설정하면 모델이 생성한 텍스트를 한 번에 반환한다.
					# True로 설정하면 모델이 텍스트를 생성하는 동안 실시간으로 결과를 스트리밍 방식으로 받을 수 있다.
}

headers = { "Content-Type": "application/json" }
response = requests.post(url, data=json.dumps(payload), headers=headers, stream=True)

if response.status_code == 200:
	print("Response:", end=" ")
	# 줄 단위로 데이터를 읽어옴
	for line in response.iter_lines():
		if line:
			# 바이트 데이터를 문자열로 디코딩
			text = line.decode('utf-8')
			result = json.loads(text)
			# 모델이 생성한 텍스트를 출력
			print(result.get("response", ""), end=" ", flush=True)

else:
	print("Error:", response.status_code, response.text)

 

토큰이 생성되는 즉시 실시간으로 출력된다.

 

 

인공지능과 연속적인 대화를 나눠 보자.

import requests
import json

url = "http://localhost:11434/api/chat" # chat은 챗봇과의 대화를 의미하는 것으로, 이 API 엔드포인트는 챗봇과의
# 상호작용을 처리하는 역할을 한다. 클라이언트가 이 URL로 POST 요청을 보내면, 서버는 챗봇 모델을 사용하여 질문에
#  대한 답변을 생성하고 이를 응답으로 반환한다.
payload = {
	"model": "llava",
	"messages": [
		{
			"role": "system",
			"content": "너는 친절한 조수야. 네 이름이 뭐야?"
		},
		{
			"role": "user",
			"content": "한국의 수도는 어디야?"
		}
	],
	"stream": False
}

headers = { "Content-Type": "application/json" }
response = requests.post(url, data=json.dumps(payload), headers=headers)

if response.status_code == 200:
	result = response.json()
	print("답변:", result.get("message", {}).get("content", ""))
	# 챗봇이 생성한 답변은 JSON 응답의 "message" 필드 내의 "content" 필드에 포함되어 있다. 따라서, result.get("message", {}).get("content", "") 코드를 사용하여 답변을 추출하고 출력한다.
	# {}는 get 메서드에서 기본값으로 빈 딕셔너리를 제공하여, "message" 키가 존재하지 않을 경우에도 오류 없이 빈 딕셔너리를 반환하도록 한다. 그리고 다시 get("content", "")를 사용하여 "content" 키가 존재하지 않을 경우 빈 문자열을 반환하도록 한다.
else:
	print("Error:", response.status_code, response.text)

 

 

 

 

이미지를 분석해 보자.

import requests
import json
import base64

# Ollama의 llava와 같은 멀티모달(시각-언어) 모델에 이미지를 전달하려면, 이미지 파일의 경로를 프롬프트에 텍스트로 적는 것이
# 아니라 이미지 파일을 읽어서 Base64 방식으로 인코딩한 뒤, 페이로드의 images 배열 매개변수로 전달해야 한다.

# 로컬 이미지 파일을 읽어 Base64로 인코딩
image_path = r"D:\D\My project\C\bus.jpg"
with open(image_path, "rb") as image_file:
	base64_image = base64.b64encode(image_file.read()).decode('utf-8')

url = "http://localhost:11434/api/generate" # 엔드포인트 URL
# generate는 모델이 텍스트를 생성하는 것을 의미한다.
# 이 엔드포인트는 모델에게 텍스트 생성을 요청하는 역할을 한다.
# 예를 들어, 사용자가 "What is the capital of France?"라는 질문을 보내면, 모델은 이에 대한 답변을 생성하여 반환하게 된다.
payload = {
	"model": "llava",
	"prompt": "이 사진은 어디서 찍었을까? 한글로 대답해줘.",
	"stream": False, # 스트리밍 여부를 설정하는 옵션으로, False로 설정하면 모델이 생성한 텍스트를 한 번에 반환한다.
	"images": [base64_image] # Base64 문자열로 인코딩된 이미지를 리스트 형태로 전달
}

headers = { "Content-Type": "application/json" }
response = requests.post(url, data=json.dumps(payload), headers=headers)

if response.status_code == 200:	
	print("Response:", response.json().get("response", "No generated text found"))
	# response.json().get("response", "No generated text found")는 JSON 응답에서 "response" 키에 해당하는 값을 가져오며,
	# 만약 해당 키가 존재하지 않을 경우 "No generated text found"라는 기본값을 반환한다.
else:
	print("Error:", response.status_code, response.text)

 

 

 

반응형
Posted by J-sean
:
반응형

Ollama를 설치하고 사용해 보자.

Documentation

API Reference

 

Windows 버전을 설치한다.

 

 

특별한 옵션도 없고 그냥 설치하면 된다.

 

help

 

아직 설치된 모델이 없다.

 

 

llava 모델을 설치하고 사용해 보자. LLava 모델은 한글 사용이 가능하지만 자연스럽지는 않다.

Large Language and Vision Assistant

 

ollama pull llava

 

 

ollama run llava를 실행하면 llava 모델이 실행된다.

 

D:\D\My project\C\bus.jpg

 

 

질문을 입력하면 답변이 출력된다.

 

답변은 영어로 나오지만 한글 질문도 알아듣는다.

 

이미지 경로를 다시 입력하지 않아도 정확히 인식한다.

 

llava 모델의 경우 자연스럽지는 않지만 한글 출력도 가능하다.

 

모델 삭제

 

※ 참고

vLLM

vLLM Documentation

vLLM GitHub

 

반응형
Posted by J-sean
:
반응형

CMOVcc (Conditional Move) 명령어를 사용해 보자.

 

CMOVE - 같으면(ZF=1) 이동한다.

 

CMOVNE - 같지 않으면(ZF=0) 이동한다.

 

eax와 ebx가 같지 않기 때문에 ecx에 eax 값 1이 저장된다.

 

eax와 ebx가 같지 않기 때문에 ecx에 eax 값 1이 저장되지 않는다.

 

반응형
Posted by J-sean
:
반응형

Intel® 64 and IA-32 Architectures Software Developer’s Manual을 확인해 보자.

 

 

RDTSC—Read Time-Stamp Counter

Reads the current value of the processor’s time-stamp counter (a 64-bit MSR) into the EDX:EAX registers. The EDX register is loaded with the high-order 32 bits of the MSR and the EAX register is loaded with the low-order 32 bits. (On processors that support the Intel 64 architecture, the high-order 32 bits of each of RAX and RDX are cleared.) The processor monotonically increments the time-stamp counter MSR every clock cycle and resets it to 0 whenever the processor is reset.

RDTSC 인스트럭션은 프로세서의 time-stamp counter를 EDX:EAX 레지스터로 가져온다. EDX에는 상위 32비트가 저장되고 EAX에는 하위 32비트가 저장된다.

 

EDX, EAX에 저장되는 값을 확인해 보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
%include "io64.inc"
 
section .text
global CMAIN
CMAIN:
    rdtsc
 
    PRINT_STRING 'High-order 32 bits of Time Stamp Counter: '
    PRINT_HEX 4, edx
    NEWLINE
    NEWLINE
    
    PRINT_STRING 'Low-order 32 bits of Time Stamp Counter: '
    PRINT_HEX 4, eax
    NEWLINE
    NEWLINE
    
    PRINT_STRING 'Time Stamp Counter: '    
    shl rdx, 32
    xor rdx, rax
    PRINT_HEX 8, rdx
 
    xor rax, rax
   ret
 

 

 

EDX, EAX의 값을 확인한다.

 

EAX 레지스터에 저장된 값을 이용해 1~10까지의 랜덤 넘버를 추출해 보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
%include "io64.inc"
 
section .text
global CMAIN
CMAIN:
    rdtsc
    
    ;eax에 저장되는 값을 사용한다.
    PRINT_STRING 'Low-order 32 bits of Time Stamp Counter (dividend): '
    PRINT_HEX 4, eax
    NEWLINE
    NEWLINE
    
    ;2byte 이상 나누기 연산시 div 는 dx, ax를 사용한다.
    ;나누어지는 값을 dx:ax에 나누어 삽입해야 하므로 edx를 0으로 만들고 eax의
    ;값 중 2바이트만 사용한다.
    xor edx, edx
    and eax, 0x0000ffff
    PRINT_STRING 'DX: '
    PRINT_HEX 2, dx
    NEWLINE
    PRINT_STRING 'AX: '
    PRINT_HEX 2, ax
    NEWLINE
    NEWLINE
    
    mov bx, 0xa ;나누는 수를 10으로 설정한다.
    PRINT_STRING 'Divisor: '
    PRINT_HEX 2, bx
    NEWLINE
        
    div bx ;bx(10)로 나눈다. 나머지는 0~9가 나온다.
    PRINT_STRING 'Quotient: '
    PRINT_HEX 2, ax ;몫은 ax에 저장된다.
    NEWLINE
    PRINT_STRING 'Remainder: '
    inc dx
    ;나머지는 dx에 저장된다. 1을 더해서 범위를 1~10으로 맞춘다.
    PRINT_HEX 2, dx
    
    xor rax, rax
   ret
 

 

 

 

Remainder로 1~10의 숫자가 무작위로 추출된다.

 

그런데 실제로 여러번 해 보면 홀수만 나오는 것을 알 수 있다. 내 컴퓨터에서만 그러는건지는 모르겠지만 rdtsc 명령어가 (EAX 레지스터에)짝수만 반환하기 때문이다. (마지막에 1을 더하므로 결국 홀수가 된다)

 

EDX 레지스터의 값을 사용해 보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
%include "io64.inc"
 
section .text
global CMAIN
CMAIN:
    rdtsc
    
    ;edx에 저장되는 값을 사용한다.
    PRINT_STRING 'High-order 32 bits of Time Stamp Counter (dividend): '
    PRINT_HEX 4, edx
    NEWLINE
    NEWLINE
    
    ;2byte 이상 나누기 연산시 div 는 dx, ax를 사용한다.
    ;나누어지는 값을 dx:ax에 나누어 삽입해야 하므로 edx의 값을 eax에 저장하고
    ;edx를 0으로 만든다. 그리고 eax의 값 중 2바이트만 사용한다.
    mov eax, edx    
    xor edx, edx
    and eax, 0x0000ffff
    PRINT_STRING 'DX: '
    PRINT_HEX 2, dx
    NEWLINE
    PRINT_STRING 'AX: '
    PRINT_HEX 2, ax
    NEWLINE
    NEWLINE
    
    mov bx, 0xa ;
    PRINT_STRING 'Divisor: '
    PRINT_HEX 2, bx
    NEWLINE
        
    div bx
    PRINT_STRING 'Quotient: '
    PRINT_HEX 2, ax
    NEWLINE
    PRINT_STRING 'Remainder: '
    inc dx
    PRINT_HEX 2, dx
 
    xor rax, rax
   ret
 

 

 

홀수, 짝수 상관없이 1~10의 숫자가 무작위로 추출된다.

하지만 EDX 레지스터의 값은 일반적인 시간의 '초' 단위와 비슷한 간격으로 바뀐다. 프로그램을 빠르게 반복 실행하면 동일한 숫자가 반복되어 나오는걸 알 수 있다. 빠른 반복 추출이 필요하다면 EAX 레지스터의 값 중 하위 2바이트가 아닌 중간 2바이트 숫자를 이용하는 등 다른 방법으로 랜덤 넘버를 추출한다.

 

※ 주의

DIV 연산시 나누는 수가 작아 몫이 너무 크게 되면 ax가 저장할 수 있는 용량(2바이트)을 넘어서게 되고 Program received signal SIGFPE, arithmetic exception 메세지가 나타난다. Floating-Point Exception을 의미하지만 0으로 나누는 연산과 같은 모든 산술 연산 에러를 포함한다.

Program Error Signals

 

SIGFPE 예외 발생시 프로그램이 멈추게 된다. 디버깅해야 메세지를 확인 할 수 있다.

 

반응형
Posted by J-sean
: