{ "cells": [ { "metadata": {}, "cell_type": "markdown", "source": [ "### Vision 모델 내부 동작 원리\n", "- 이미지 인코딩 : 이미지 임베딩 벡터\n", "- 프로젝션 : 이미지 벡터를 LLM 토큰 공간으로 변환\n", "- 토큰 결합 : 시각 토큰 + 텍스트 토큰을 순서대로 배열\n", "- LLM 처리\n", "- 텍스트 생성" ], "id": "3ed294a9c2fd2cf5" }, { "cell_type": "code", "id": "initial_id", "metadata": { "collapsed": true, "ExecuteTime": { "end_time": "2026-06-09T08:40:13.650962123Z", "start_time": "2026-06-09T08:40:09.151285584Z" } }, "source": [ "import base64\n", "from langchain_ibm import WatsonxEmbeddings\n", "from langchain_ibm import ChatWatsonx\n", "\n", "from dotenv import load_dotenv\n", "from langchain_core.prompts import ChatPromptTemplate\n", "from langchain_core.output_parsers import StrOutputParser\n", "\n", "from langchain_chroma import Chroma\n", "import os\n", "\n", "from langchain_ollama import ChatOllama\n", "from pathlib import Path\n", "\n", "from langchain_core.documents import Document\n", "from langchain_core.messages import HumanMessage" ], "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/home/cooney/Source/.venv/lib/python3.12/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", " from .autonotebook import tqdm as notebook_tqdm\n" ] } ], "execution_count": 1 }, { "metadata": { "ExecuteTime": { "end_time": "2026-06-09T08:40:13.666828279Z", "start_time": "2026-06-09T08:40:13.654303979Z" } }, "cell_type": "code", "source": [ "# .env 내용 가져오기\n", "load_dotenv()\n", "\n", "apikey = os.getenv(\"WATSONX_API_KEY\")\n", "project_id = os.getenv(\"WATSONX_PROJECT_ID\")\n", "watsonx_ai_url = os.getenv(\"WATSONX_URL\")" ], "id": "27bd39a0da002140", "outputs": [], "execution_count": 2 }, { "metadata": { "ExecuteTime": { "end_time": "2026-06-09T08:40:17.037718935Z", "start_time": "2026-06-09T08:40:13.668291706Z" } }, "cell_type": "code", "source": [ "watson_llm = ChatWatsonx(\n", " model_id=\"ibm/granite-4-h-small\",\n", " url=f\"{watsonx_ai_url}\",\n", " api_key=f\"{apikey}\",\n", " project_id=f\"{project_id}\",\n", " max_tokens=2000,\n", " params={\n", " \"temperature\": 0\n", " }\n", ")\n", "\n", "watson_embedding = WatsonxEmbeddings(\n", " model_id=\"ibm/granite-embedding-278m-multilingual\",\n", " url=f\"{watsonx_ai_url}\",\n", " api_key=f\"{apikey}\",\n", " project_id=f\"{project_id}\"\n", ")\n", "\n", "# 로컬 LLM\n", "qwen_llm = ChatOllama(model=\"qwen3.5:4b\", temperature=0)\n", "exaone_llm = ChatOllama(model=\"exaone3.5:2.4b\", temperature=0)\n", "gemma_llm = ChatOllama(model=\"gemma4:e2b\")" ], "id": "1b23141c1b60154a", "outputs": [], "execution_count": 3 }, { "metadata": {}, "cell_type": "markdown", "source": "#### 1. 로컬 이미지", "id": "ef95d69bf02f830b" }, { "metadata": { "ExecuteTime": { "end_time": "2026-06-09T08:40:17.084968096Z", "start_time": "2026-06-09T08:40:17.063355507Z" } }, "cell_type": "code", "source": [ "# images를 base64 인코딩\n", "\n", "def encode_image(image_path:str)->str:\n", " with open(image_path, \"rb\") as f:\n", " return base64.b64encode(f.read()).decode(\"utf-8\")" ], "id": "637e09cdf9c78eee", "outputs": [], "execution_count": 4 }, { "metadata": { "ExecuteTime": { "end_time": "2026-06-09T08:40:17.236651215Z", "start_time": "2026-06-09T08:40:17.091569162Z" } }, "cell_type": "code", "source": [ "# vision 모델\n", "vision_llm = ChatOllama(model=\"minimax-m3:cloud\", temperature=0)\n", "\n", "parser = StrOutputParser()\n", "\n", "# 이미지 + 텍스트 : 이미지 기반으로 질문\n", "def ask_about_image(image_path, question):\n", " image_b64 = encode_image(image_path)\n", " prompt = ChatPromptTemplate.from_messages(\n", " [\n", " (\"human\",\n", " [\n", " {\"type\":\"image_url\", \"image_url\":{'url':f'data:image/jpeg;base64,{image_b64}'}},\n", " {\"type\":\"text\", \"text\":question}\n", " ]\n", " )\n", " ]\n", " )\n", "\n", " chain = prompt | vision_llm\n", " response = chain.invoke({\"image_b64\":image_b64, \"question\":question})\n", " return response" ], "id": "a1114bd96f354025", "outputs": [], "execution_count": 5 }, { "metadata": { "ExecuteTime": { "end_time": "2026-06-09T08:41:42.833366570Z", "start_time": "2026-06-09T08:41:20.436085763Z" } }, "cell_type": "code", "source": "print(ask_about_image(\"./image/animals1.jpg\", \"이 이미지에서 무엇이 보이나요?\"))", "id": "25f2f6918598e8e0", "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "content='이미지에는 **고양이**가 보입니다. 구체적인 특징을 살펴보면:\\n\\n- **갈색 줄무늬 고양이(태비)**로, 회색과 갈색이 섞인 줄무늬 무늬를 가지고 있습니다.\\n- **아름다운 청록색(옅은 파란색)의 눈동자**이 매우 인상적입니다.\\n- 고양이가 **입을 살짝 벌리고** 있어서 마치 울려고 하거나 야옹거릴 것 같은 표정을 짓고 있습니다.\\n- **긴 흰색 수염**이 양옆으로 길게 뻗어 있습니다.\\n- 고양이는 **나무 바닥** 위에 있는 것처럼 보이며, 따뜻한 **오렌지색 톤의 나무 결**이 배경에 드러나 있습니다.\\n- 위에서 내려다보는 각도로 촬영되어 고양이의 얼굴이 화면을 가득 채우고 있습니다.\\n\\n전체적으로 아기자기하면서도 고양이의 생기 넘치는 표정이 잘 포착된 사진입니다. 🐱' additional_kwargs={} response_metadata={'model': 'minimax-m3', 'created_at': '2026-06-09T08:41:42.722793354Z', 'done': True, 'done_reason': 'stop', 'total_duration': 21061167608, 'load_duration': None, 'prompt_eval_count': 511, 'prompt_eval_duration': None, 'eval_count': 237, 'eval_duration': None, 'logprobs': None, 'model_name': 'minimax-m3', 'model_provider': 'ollama'} id='lc_run--019eab8b-199f-7811-bea5-c25c73d68d14-0' tool_calls=[] invalid_tool_calls=[] usage_metadata={'input_tokens': 511, 'output_tokens': 237, 'total_tokens': 748}\n" ] } ], "execution_count": 8 }, { "metadata": {}, "cell_type": "code", "source": "#### 2. URL 이미지", "id": "79877a4f0166e70d", "outputs": [], "execution_count": null }, { "metadata": { "ExecuteTime": { "end_time": "2026-06-09T08:42:15.301506904Z", "start_time": "2026-06-09T08:41:45.482282423Z" } }, "cell_type": "code", "source": [ "import requests\n", "from io import BytesIO\n", "from PIL import Image\n", "\n", "# 이미지 다운로드\n", "url ='https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQK4_aoipvzj-4LsUx-FAtPmRnkgtOOMbRAQOfBceT5UQ&s=10'\n", "img = requests.get(url).content\n", "# 인코딩\n", "img_b64 = base64.b64encode(img).decode(\"utf-8\")\n", "\n", "# 모델\n", "message = HumanMessage(\n", " content = [\n", " {\"type\":\"image_url\", \"image_url\":{'url':f'data:image/jpeg;base64,{img_b64}'}},\n", " {\"type\":\"text\", \"text\": \"이 동물의 종류와 특징을 설명해주세요.\"}\n", " ]\n", " )\n", "\n", "vision_llm.invoke([message]).content" ], "id": "bcc767a8ef31098b", "outputs": [ { "data": { "text/plain": [ "'# 고양이 품종 분석\\n\\n이미지의 고양이는 **시베리안(Siberian)** 또는 **노르웨이 숲고양이(Norwegian Forest Cat)**로 보입니다. 특히 풍성한 이중 피모(double coat)와 체형으로 미루어 **시베리안 고양이**일 가능성이 높습니다.\\n\\n## 🐱 품종 특징\\n\\n### 외모\\n- **풍성한 장모**: 추운 서식지 환경에 적응한 이중 구조의 털\\n- **색상**: 화이트와 그레이의 클래릭 터비(고등어) 패턴\\n- **눈**: 크고 둥근 눈, 황금색~녹색 계열\\n- **체형**: 중대형의 다부진 몸매, 굵은 뼈대\\n\\n### 성격적 특징\\n- **온화하고 친근한 성격**: \"고양이계의 골든 리트리버\"라고 불릴 정도로 사람 친화적\\n- **높은 지능**: 문제 해결 능력이 뛰어나고 훈련이 잘 됨\\n- **사교적**: 다른 반려동물이나 아이들과도 잘 어울림\\n- **활발함**: 운동 능력이 뛰어나고 놀이를 좋아함\\n- **물에 대한 친화력**: 대부분의 고양이와 달리 물을 무서워하지 않음\\n\\n### 건강 관련\\n- **알레르기 저감형**: 다른 고양이 품종에 비해 **Fel d 1 단백질** 분비량이 적어 알레르기 반응이 적다고 알려져 있음\\n- **평균 수명**: 12~15년\\n- **유전 질환이 적어** 비교적 건강한 편\\n\\n### 역사\\n시베리안 고양이는 러시아 시베리아 지역이 원산지로, 수백 년 이상 야생에서 살아온 고양이가 길들여진 품종입니다. 자연 환경에 적응하며 진화한 대표적 토종 고양이 중 하나입니다.'" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "execution_count": 9 }, { "metadata": { "ExecuteTime": { "end_time": "2026-06-09T08:42:15.314335059Z", "start_time": "2026-06-09T08:42:15.303693807Z" } }, "cell_type": "code", "source": "#### 3. OCR", "id": "9549854d2a4d9c89", "outputs": [], "execution_count": 10 }, { "metadata": { "ExecuteTime": { "end_time": "2026-06-09T08:42:15.335585069Z", "start_time": "2026-06-09T08:42:15.316787126Z" } }, "cell_type": "code", "source": [ "# 모든 이미지를 jpeg 저장 / 인코딩\n", "\n", "def pil_to_base64(img:Image.Image,format=\"JPEG\")->str:\n", " buffer = BytesIO()\n", " img.save(buffer, format=format)\n", "\n", " return base64.b64encode(buffer.getvalue()).decode(\"utf-8\")" ], "id": "32f3f6d75760fcec", "outputs": [], "execution_count": 11 }, { "metadata": { "ExecuteTime": { "end_time": "2026-06-09T08:49:33.136027357Z", "start_time": "2026-06-09T08:49:09.992205257Z" } }, "cell_type": "code", "source": [ "img = Image.open(\"./image/system.png\")\n", "\n", "# 리사이즈\n", "img_resized = img.resize((800,600))\n", "\n", "# Gray\n", "img_gray = img_resized.convert(\"L\")\n", "img_b64 = pil_to_base64(img_gray)\n", "\n", "message = HumanMessage(\n", " content = [\n", " {\"type\":\"text\", \"text\":\"이 문서의 텍스트를 추출해주세요\"},\n", " {\"type\":\"image_url\", \"image_url\":{'url':f'data:image/jpeg;base64,{img_b64}'}},\n", "\n", " ]\n", " )\n", "\n", "result = vision_llm.invoke([message]).content\n", "print(result)" ], "id": "18e51689e189291f", "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "# 문서 텍스트 추출\n", "\n", "## 미래로봇추진단(서울 근무)\n", "### S/W개발 - 시스템 소프트웨어\n", "\n", "---\n", "\n", "### 포지션 소개 (Job Overview)\n", "휴머노이드 로봇의 실시간 제어 시스템 및 소프트웨어 플랫폼을 개발하는 직무입니다. 운영체제 환경 구성, 디바이스 드라이버, 실시간 제어 프레임워크 등 로봇 동작의 핵심 기반이 되는 소프트웨어를 설계 및 개발하며, 하드웨어 설계 조직 및 AI 연구 조직과 긴밀히 협업합니다.\n", "\n", "### 수행업무 (Job Details)\n", "- 휴머노이드 로봇의 실시간 제어 프레임워크를 설계하고 개발합니다.\n", "- 로봇, 센서 등 로봇 하드웨어 제어를 위한 디바이스 드라이버 및 하드웨어 추상화 계층을 개발합니다.\n", "- 로봇 제어를 운영체제(Linux 기반 실시간 OS 등) 환경을 구성하고 시스템 성능을 최적화합니다.\n", "- 유관 부서와 협업하여 로봇 조직 등 시 기능과 제어 시스템 간 연동 미들웨어를 개발합니다.\n", "\n", "### 자격요건 (Requirements)\n", "- 컴퓨터, 전기·전자, 기계, 로봇공학 등 관련 전공을 하신 분\n", "- 운영체제 기반 개념에 대한 이해도를 보유하신 분\n", "- 요구사항을 분석하여 소프트웨어를 구조적으로 설계 및 구현하는 역량을 보유하신 분\n", "- 다양한 분야의 엔지니어와 적극적으로 소통하며 협업할 수 있는 역량을 보유하신 분\n", "\n", "### 우대사항 (Preferences)\n", "- C/C++ 기반 시스템 프로그래밍 역량을 보유하신 분\n", "- Linux 기반 임베디드 시스템 개발 경험을 보유하신 분 (Kernel, Device Driver, BSP 등)\n", "- CAN, EtherCAT, UDP 등 하드웨어 통신 프로토콜 활용 경험을 보유하신 분\n", "- ROS2 기반 로봇제어 소프트웨어 수행 경험을 보유하신 분\n", "- Git 기반 협업 및 CI/CD 환경에서의 개발 경험을 보유하신 분\n", "\n", "### 커리어 비전 (Career Vision)\n", "휴머노이드 로봇의 초기 개발 단계부터 참여하여 핵심 개발자로 성장할 수 있습니다. 실시간 제어, 시스템 아키텍처, 로봇 미들웨어 등 다양한 경험을 통해 로봇제어 시스템 SW 전문가, 나아가 시스템 아키텍트로의 커리어 확장이 가능합니다.\n", "\n", "---\n", "\n", "> **\"휴머노이드의 미래를 함께 실현할 분을 찾습니다\"**\n", "> \n", "> 로봇이 인공적으로 동작하기 위한 소프트웨어 기반을 함께 만들어 가며, 도전적인 기술 과제를 즐기는 분의 지원을 환영합니다.\n" ] } ], "execution_count": 17 }, { "metadata": { "ExecuteTime": { "end_time": "2026-06-09T08:58:04.822594430Z", "start_time": "2026-06-09T08:58:04.802857234Z" } }, "cell_type": "code", "source": [ "# 다중 이미지 비교분석\n", "\n", "def compare_images(image_paths, question):\n", " content = []\n", "\n", " for i, path in enumerate(image_paths, 1):\n", " img_b64 = encode_image(path)\n", " content.append({\"type\":\"image_url\", \"image_url\":{'url':f'data:image/jpeg;base64,{img_b64}'}})\n", " content.append({\"type\":\"text\", \"text\": f\"[이미지 {i}]\"})\n", "\n", " # 질문 추가\n", " content.append({\"type\":\"text\", \"text\":question})\n", " message = HumanMessage(content=content)\n", " response = vision_llm.invoke([message])\n", " return response.content" ], "id": "d184430da51ccc36", "outputs": [], "execution_count": 20 }, { "metadata": { "ExecuteTime": { "end_time": "2026-06-09T08:58:41.448285681Z", "start_time": "2026-06-09T08:58:04.941406663Z" } }, "cell_type": "code", "source": [ "result = compare_images(['./image/fridge.jpg', './image/table.jpg'], \"두 제품의 차이점을 비교 분석해 주세요.\")\n", "print(result)" ], "id": "baa2ba7a8d4a6b10", "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "# 두 제품의 비교 분석\n", "\n", "## 📦 제품 1: 냉장고 (이미지 1)\n", "\n", "| 항목 | 내용 |\n", "|------|------|\n", "| **제품 유형** | 4도어 프렌치 도어 냉장고 |\n", "| **외관 색상** | 화이트/크림 베이지 톤 |\n", "| **구조** | 상단 2도어 + 하단 2도어 (중앙 검은색 손잡이 바) |\n", "| **용도** | 식료품 보관 (냉동/냉장) |\n", "| **소재** | 금속 외관, 매트한 마감 |\n", "| **공간 배치** | 부엌에 독립적으로 설치 |\n", "\n", "## 🍽️ 제품 2: 식탁/테이블 (이미지 2)\n", "\n", "| 항목 | 내용 |\n", "|------|------|\n", "| **제품 유형** | 4인용 다용도 식탁 (다이닝 테이블) |\n", "| **외관 색상** | 화이트 대리석 무늬 상판 + 블랙/골드 다리 |\n", "| **구조** | 직사각형 상판 + 4개의 슬림 다리 |\n", "| **용도** | 식사, 작업, 다용도 테이블 |\n", "| **소재** | 석재/대리석 패턴 (MDF 또는 천연석 추정) + 금속 다리 |\n", "| **공간 배치** | 거실/다이닝 공간에 배치 |\n", "\n", "---\n", "\n", "## 🔍 핵심 차이점 비교\n", "\n", "### 1️⃣ **기능적 차이**\n", "- **냉장고**: 식품 보관·냉각이라는 **단일 기능성 가전**\n", "- **식탁**: 식사·작업·소통이 이루어지는 **생활 중심 가구**\n", "\n", "### 2️⃣ **디자인 철학**\n", "- **냉장고**: 미니멀하고 깔끔한 모노톤, 현대 주방 인테리어에 맞춤\n", "- **식탁**: 모던 럭셔리 스타일, 대리석 + 골드 디테일로 **고급스러움 강조**\n", "\n", "### 3️⃣ **사용 맥락**\n", "- **냉장고**: 매일 사용되는 필수 가전 (실용성 중심)\n", "- **식탁**: 가족 모임, 게스트 접대 등 **라이프스타일 연출**\n", "\n", "### 4️⃣ **가격대**\n", "- **냉장고**: 일반적으로 4도어 모델은 **200~500만 원대** 이상\n", "- **식탁**: 소재와 브랜드에 따라 **30~200만 원대**로 다양\n", "\n", "### 5️⃣ **공간 점유**\n", "- **냉장고**: 벽면 붙박이로 수직 공간 활용\n", "- **식탁**: 공간 중앙에 위치하며 **수평적 공간 활용**\n", "\n", "---\n", "\n", "## 💡 공통점\n", "두 제품 모두 **미니멀한 화이트 톤**으로 통일되어 있어 모던한 인테리어에 적합하며, 깔끔하고 세련된 실내 공간을 연출하는 데 기여하는 **생활 필수 아이템**입니다.\n" ] } ], "execution_count": 21 }, { "metadata": { "ExecuteTime": { "end_time": "2026-06-09T08:59:26.775983373Z", "start_time": "2026-06-09T08:58:56.843217983Z" } }, "cell_type": "code", "source": [ "result = compare_images(['./image/chart1.png', './image/chart2.png'], \"두 제품의 차트를을 비교하여 주요 변화 추이를 설명해주세요.\")\n", "print(result)" ], "id": "891a618c4e36fea9", "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "# 두 차트 비교 분석\n", "\n", "## 📊 차트 1: 최근 6개월 전체 화장품 수출 추이\n", "\n", "| 기간 | 수출액(억 달러) | YoY 증감률 |\n", "|------|----------------|-----------|\n", "| 25년 12월 | 883.7 | +10.2% |\n", "| 26년 1월 | 841.6 | **+33.7%** (최고점) |\n", "| 26년 2월 | 752.1 | +1.7% (최저점) |\n", "| 26년 3월 | 960.4 | +23.0% |\n", "| 26년 4월 | **1,096.3** (최고 수출액) | +21.7% |\n", "| 26년 5월(1~20일) | 671.1 | **-16.0%** (마이너스 전환) |\n", "\n", "## 📊 차트 2: 5월 1~20일 주요 국가별 일평균 수출액\n", "\n", "| 국가 | 일평균 수출액(백만 달러) | YoY 증감률 |\n", "|------|-------------------------|-----------|\n", "| 중화권(중국) | 13.13 | **+76.4%** |\n", "| 유럽 | 11.14 | +61.3% |\n", "| 미국 | 11.66 | +40.3% |\n", "| 동남아 | 4.91 | +22.0% |\n", "| 일본 | 약 3.10 | **-14.1%** |\n", "\n", "---\n", "\n", "## 🔍 주요 변화 추이 분석\n", "\n", "### 1. **4월까지 성장 → 5월 급격한 반전**\n", "- 1~4월 동안 꾸준한 성장세(YoY +21~34%)를 유지하며 4월에 **사상 최고치 1,096.3억 달러**를 기록\n", "- 그러나 5월(1~20일)에 들어 **YoY -16.0%로 마이너스 전환**되며 성장세 급격히 둔화\n", "\n", "### 2. **국가별 양극화 현상**\n", "- **📈 신흥 시장 강세**: 중화권(+76.4%), 유럽(+61.3%), 미국(+40.3%) 등 주요 시장에서 고른 성장\n", "- **📉 일본 시장 침체**: 유일하게 마이너스(-14.1%)를 기록하며 전체 수출액 둔화의 주요 원인으로 작용\n", "\n", "### 3. **성장 동력의 재편**\n", "- 종전에는 일본이 주요 화장품 수출 대상국이었으나, 일본 시장 위축이 전체 지표에 영향\n", "- **중화권·유럽·미국 중심의 수출 구조 재편**이 가속화되는 흐름\n", "\n", "### 4. **시사점**\n", "- 5월 마이너스 전환은 **일본 시장 감소**가 가장 큰 영향 요인\n", "- 단, 5월은 1~20일(부분 집계) 기간이므로 **후반부 회복 여부** 추가 모니터링 필요\n", "- 한·중·일 관계 변화, 환율, 관세 등 외부 변수 영향 지속 점검 요구됨\n" ] } ], "execution_count": 24 }, { "metadata": {}, "cell_type": "code", "outputs": [], "execution_count": null, "source": "", "id": "972db1f4ffac7c6f" } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 2 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", "version": "2.7.6" } }, "nbformat": 4, "nbformat_minor": 5 }