과적합: 에포크를 과도하게 늘리면 학습데이터셋에서는 높은 정확도를 보이지만 새로운 데이터셋에서 일반화 되지 못할 수 있음
Catastophic Forgetting: 특정한 작업을 위한 미세조정 과정에서 처음에 습득한 광범위한 지식을 잃을 위험
데이터 유출: 훈련 및 검증 데이터 세트가 중복됨
RAG
정의: 검색기반 모델과 생성 모델의 장점을 결합, 입력 쿼리를 기반으로 대규모 데이터베이스 또는 지식 기반을 검색하여 관련 정보를 검색
동적 지식 통합: 외부 소스의 실시간 정보를 통합하여 최신 지식이나 특정 지식이 필요한 작업에 적합
문맥적 연관성: 검색된 문서에서 추가적인 맥락을 제공하여 생성 모델의 응답을 향상
다재다능: 더 광범위한 쿼리를 처리할 수 있음
2. Langchain
LLM 기반 어플리케이션 개발을 간소화하고 유연하게 만드는 핵심적인 역할
2.1 Runnable
Chain을 구성하는 모든 구성요소를 연결하고 실행하기 위한 표준 인터페이스
표준화된 메소드: invoke, batch, stream 표준 호출 메소드를 제공
모든 구성요소가 공통된 메서드를 가지게 하는 것
2.2 LCEL
LangChainExpressionLanguage Runnable을 체인으로 구성하여 유닉스 파이프 연산자처럼 선언적인 방식 제공
Prompt | model | output_parser 형식으로 구성
프롬프트 엔지니어링 기법 지원
역할 기반 메시지: ChatPromptTemplate.from_messages 메소드를 사용하여 Human, AI, System 와 같은 역할 기반 메시지를 명시적으로 정의
퓨삿 프롬프팅: FewShotChatMessagePromptTemplate 모델 동작에 영향에 미치는 예시를 사전에 제공할 수 있음
출처: Prompt Engineering vs. RAG vs. Finetuning: What’s the Difference?
StringOutputParser
언어모델의 출력값을 다루기 쉬운 문자열로 변환하기 위해 사용함
LLM이나 챗봇 모델은 단순히 텍스트만 변환하는 것이 아니라 AIMessage, ChatMessage와 같은 복잡한 객체 형태로 출력하기 때문에 순수한 텍스트 내용을 추출하기 위한 방법
3. 기타
3.1. Pydantic 사용하는 이유
파이썬은 동적언어여서 자료형이 사전에 선언되지 않음
타입 힌트는 힌트일 뿐이며 데이터 유효성 검사를 위해 Pydantic를 사용하여 출력 자료형을 구체하하는것
3.2. RAG을 사용해야하는 이유
지식의 단절, 특정 시점까지만 LLM이 학습하기 때문
context window의 한계, 기억 메모리가 무한하지 않음
벡터 DB는 기존 DB가 정확히 요청한 단어만 찾는것과 달리, 유사한 단어를 찾아 응답결과를 좀더 풍성하게 해줌
벡터(색인)과 원본데이터(원자료)를 모두 저장하여 유사한 색인을 찾아 결과를 반환하는 형태로 구현됨
3.3. 메모리를 기억하기 위한 방법
이 부분 확실하지 않음. 애초에 LLM은 stateless 하기 떄문에 정보를 저장하는게 중요.chat_message 메소드에 이전 대화 기록을 AI message, Human Message등을 저장하고 멀티턴으로 구현하는 방법도 있으나 context limt 때문에 적절하지 않을 수 있음. 검색한 결과 다음 메서드가 있는 것을 확인
자료작성, 블로글 ok, 책 밑줄 그으면서 설명해도되고, 자료(스샷)가져와서 판서하면서해도됨
발표자: 매주 3명 발표 필수(설명을 하는 5분-10분)
참여자: 3줄 요약으로 경험 공유
디포짓 5만원 선입금
발표 안하면 만원 차감
참여 안하면 오천원 차감, 1회 패스권 사용 가능
시스템
발표 순서 정리
예치금 현황판
총 6단원으로 이루어진 책이기 때문에 12명을 구인했습니다. 각 단원을 절반씩 쪼개 1주차당 3명의 발표를 의무적으로 잡았습니다. 단순히 읽고 논의하기 라는 느슨한 규칙을 세우면 책을 안 읽을 수 도 있기 때문입니다. 하지만 발표의 결과물 형태를 까다롭게 하지 않았습니다. 각자의 개인노션이나 블로그에 작성한 화면 그대로를 발표로 해도 되고 관심있게 본 단어나 구절만 발표하는 식으로도 가능하게 했습니다. 실제로 이렇게 진행하니 총 36회의 발표에서는 미발표는 2번, 미참석은 2번이였습니다.(참석 패스는 9명 사용)
3. 스터디 회고
다음은 12주간 스터디 진행 후 KPT 템플릿을 이용한 후기를 Gemini Pro 2.5를 통해 요약한 결과 입니다.
Keep (성과 및 유지할 점)
성공적 스터디 시스템
주 3인 발표 제도로 책임감 부여 및 꾸준한 학습 동기 마련
발표자 외 '3줄 요약' 규칙으로 참여도 유지
예치금 제도로 완주 의지 강화
자율적 발표 형식으로 다양한 시각(블로그, 시각화 등) 공유 및 학습 효과 증대
긍정적 학습 경험
500p 분량의 기술 서적 완주 경험 획득
동료 학습을 통해 혼자서는 어려웠을 개념(캐시라인, 콜백 등) 명확히 이해
다양한 발표 스타일 덕분에 다각적이고 입체적인 학습 가능
AI(Claude, LLM)를 활용한 시각화, 내용 검증 등 새로운 학습법 시도
Problem (문제점 및 아쉬움)
스터디 운영의 비효율
3인 발표 시 내용이 중복되어 집중력 저하 및 비효율 발생
발표자가 아닐 경우, 학습 우선순위가 밀려 수동적 참여에 그치는 경향 발생
발표 중심의 단방향 소통으로 상호작용(질문, 토론) 부족
학습 깊이의 한계
C/C++ 코드 예제를 눈으로만 훑고 넘어가 실질적인 체득 부족
어려운 주제 발생 시 깊게 파고들지 못하고 수박 겉핥기식으로 학습
개인 실습 및 학습 내용을 블로그 등으로 정리하는 Output 활동 부족
Try (향후 시도할 점)
스터디 운영 방식 개선
(역할 분담) 발표 전, 이론/코드분석/심화탐구 등 파트를 나눠 중복 방지
(상호작용 강화) 챕터별 질의응답 시간을 의무화하거나, 나무위키처럼 공동 학습 노트 작성
(참여 유도) 비발표자도 참여할 수 있는 퀴즈나 토론 주제 제시
개인 학습 전략 강화
(코드 실습) 예제 코드를 직접 실행하거나, 익숙한 언어(Python 등)로 변환하여 실습
데이터 중심 어플리에케이션 설계(a.k.a 대중애설, DDIA) 스터디 이후 다양한 스터디원들의 공통 관심사인 CS로 책을 선정하게 되었습니다. 처음에는 중국인 저자여서 선입견을 가지고 시작했는데, 각 개념에 필요한 적절한 그림과 명확한 설명에 한 번 놀라고, 컴파일러부터 시작하여 메모리,프로세스, 스레드, 캐시, cpu, 입출력까지 차근히 지식의 상아탑을 쌓아가는 것을 보고 한 번 더 놀랐습니다. 앞으로 이 저자의 책은 고민안하고 읽어볼 것 같습니다.
필자는 데이터 분석가로 일을 시작했고 컴퓨터과학개론만 1학년 때 수강했으므로 CS의 지식은 거의 없지만, 하드웨어를 만지는걸 좋아해서 용어나 개념에 대한 이해가 막 어색하거나 낯설진 않았습니다. 오히려 희미한 지식에서 가끔 들리는 프로세스/스레드 코루틴, 동기/비동기와 같은 개념을 한 번 잡아볼 수 있는 기회여서 스터디에 대한 만족도는 높았습니다. 본인이 CS에 대한 이해도가 부족하거나 한 번 정리하길 원한다면 일독을 추천드립니다.
개인적으로 인상깊게 보았던 개념이 "데이터 일관성"에 대한 개념입니다. 사실 설계 개발자의 관점에서 중요한 부분이긴 하나, 프로세스/스레드에서 데이터 일관성의 이슈가 분산 시스템에서도 이식되어 똑같은 문제상황이 일어나고 유사한 방법으로 해결하려고 한다는 점이 흥미로웠습니다. 시대가 발전해도 고전적인 프로그래밍의 설계와 철학은 그 이유가 있므르로 역시 과거는 미래의 등대라는 생각이 듭니다. 여기에 더하여 AI agent에서는 서로 다른 기능을 하는 Agent가 공통된 Context를 유지하기 위한 시스템 프롬프트를 공유하는 부분이 중요하다고 들었는데 나아가 이런 데이터 일관성에 대한 부분이 새로운 기술에도 적용되는 뼈대구나 라고 생각이 들면서도 기본을 알면 여기저기에 써먹을 것이 많구나 라는 생각도 동시에 들었습니다.
종합적으로 볼 때, 장기적인 저의 커리어 목표(Chief Data Owner)에 엔지니어링 축이 될 수 있는 바탕이 되었고, 앞으로 진행할 상아탑에도 튼튼한 버팀목이 되었으면 좋겠습니다. 🙂
개발자에게는 코딩 테스트를 요구하는 것이 당연하게 여겨집니다. 하지만 데이터 직군(분석, 과학, AI) 직무는 회사 상황이나 요구사항에 따라 Python 알고리즘부터, 라이브러리를 이용한 문제 해결, 과제 전형 등 다양한 방식으로 진행됩니다. 이런 상황에서 데이터 직군은 알고리즘을 알아야할까요? 이번 글은 문제를 실제로 보면서 푸는 로직을 공유하고 실제로 데이터 과학자에게 알고리즘 공부가 필요한지 논의 해보는 글입니다.
def solution(k, m, score):
score.sort(reverse=True) #사과 점수 내림차순으로 정렬
result = 0
while len(score) >= m:
box = score[:m] #사과를 점수가 높은 순부터 4개 뽑기
result += min(box)*m #4개 뽑은 것 중에 가장 낮은 점수 곱하기 * 사과개수
del score[:m] #꺼낸 사과 제거
return result
실행 결과
학생질문) 시간 초과로 일부 테스트는 실패라고 뜨는데 왜 그런지 궁금합니다. 그럼 이 문제는 다른 방법으로 풀어야 하는 것일까요?
2. 나의 대답 전문
2.1 알고리즘 채점 방식
파이썬 알고리즘 문제는 후반으로 갈수록 좀 정교한 방식으로 채점됩니다. 문제를 해결하기 위한 논리와 효율성이 그것입니다. 위 코드에서는 효율성이 문제가 됩니다.보통 알고리즘 문제는 1초안에 수행되어야합니다. 위 코드를 돌려보면 6번 문제부터 100ms가 소요되는 걸 확인할 수 있어요. 그렇습니다. 코드 어딘가에 굉장히 속도가 많이 걸리는 것을 알 수 있습니다.
2.2 현 코드의 문제
결론만 말하자면, 반복문 혹은 정렬 등에 관련된 구문이 속도를 잡아 먹습니다. 위 코드에서는del score[:m]구문입니다. score가 가장 큰 경우는 100만개입니다. 반면 최소값은 m이 3입니다. 이런 최악의 경우 다음과 같이 진행됩니다.
사과 상자를 정렬함 (<- 사실 이 정렬속도도 매우 큽니다.)
100만개의 사과 중 3개를 뽑아냄. 그 이후 나머지 사과를 앞으로 3칸씩 "당김"
999,997 개의 사과중 3개를 뽑아냄. 그 이후 나머지 사과를 앞으로 3칸씩 "당김"
반복
del 연산이 자체적으로 데이터를 앞 당기는 상황입니다. 100만개를 모두요! 언제까지? while문이 끝날 때 까지. 그래서 문제가 되고 실제로 다음 코드를 돌려보면 시간이 매우 걸리는 것을 확인할 수 있습니다. 문제 11번 부터 시간초과라고 뜨는 이유가 해당 테스트에서 score길이가 길어서 그래요! 더 깊게 알고 싶다면시간복잡도개념을 검색해보시면 됩니다.
2.3. 개선 코드
기존 코드를 바꾸는 것도 고민해봤지만, for문으로 변형하는게 제일 합리적입니다. 일정한 사과의 갯수를 포장하는 것은range(start, end, step)함수를 이용해서 만들 수 있거든요.
def solution(k, m, score):
score.sort(reverse=True)
result = 0
# m-1 인덱스부터 리스트 끝까지 m씩 건너뛰며 반복
# 이 인덱스들이 각 상자의 최저 점수가 됨
for i in range(m - 1, len(score), m):
result += score[i] * m
return result
위 코드를 돌리면 기존 문제 6번이 100ms 에서 6ms 로 16배 빨라졌습니다!
2.4. 검증해보기
100만개의 사과를 테스트 해보면 시간이 너무 오래걸려서 10만개로 가정하고 테스트해봅니다. chagpt한테 만들어달라고 하면 금방 만들어줘요
import time
import random
# 1. 기존의 while문 방식
def solution_original(k, m, score):
score.sort(reverse=True) #사과 점수 내림차순으로 정렬
result = 0
while len(score) >= m:
box = score[:m] #사과를 점수가 높은 순부터 4개 뽑기
result += min(box)*m #4개 뽑은 것 중에 가장 낮은 점수 곱하기 * 사과개수
del score[:m] #꺼낸 사과 제거
return result
# 2. 개선된 for문 방식
def solution_improved(k, m, score):
score.sort(reverse=True)
result = 0
for i in range(m - 1, len(score), m):
result += score[i] * m
return result
# 테스트 데이터 생성
k = 9 # 사과 최대 점수 (예시)
m = 3 # 한 상자에 담는 사과 개수
# 1~k 사이의 점수를 가진 100만개의 사과 리스트 생성
score_list = [random.randint(1, k) for _ in range(100000)]
# --- 기존 방식 시간 측정 ---
# 원본 리스트를 복사해서 사용 (sort가 리스트를 변경하므로)
score_copy_1 = score_list[:]
start_time_1 = time.time()
solution_original(k, m, score_copy_1)
end_time_1 = time.time()
print(f"🐢 기존 방식 실행 시간: {end_time_1 - start_time_1:.6f}초")
# --- 개선된 방식 시간 측정 ---
score_copy_2 = score_list[:]
start_time_2 = time.time()
solution_improved(k, m, score_copy_2)
end_time_2 = time.time()
print(f"🚀 개선된 방식 실행 시간: {end_time_2 - start_time_2:.6f}초")
'''
🐢 기존 방식 실행 시간: 0.202289초
🚀 개선된 방식 실행 시간: 0.004085초
대략 50배정도 빠름!
'''
2.5. 이렇게까지 해야하나...
이런 해설을 보면 사실 이렇게까지 공부해야하나 싶으실거에요. 사실 알고리즘은 소위 개발자를 위한 테스트 시험이고, 우리가 sql을 공부하는 것처럼 준비해야하는 하나의 관문입니다. 분야도 depth 도 깊어요! 전공과목으로 있을 정도니까요근데 데이터 분석가의 지원하는 기업에 따라 일부 파이썬 기본문제를 요구하는 곳이있어요. 이런 "완전 구현" 문제(LG전자 전형)나 scikit learn을 활용한 유클리드 거리 계산(게임회사 크래프톤)이나 pandas를 활용한 문제들 출제합니다. 정말 중구 난방이긴해요 ㅋㅋ알고리즘을 푸는 것은 좋지만 너무 매달리고 개발자까지 공부할 필요는 없다는게 제 결론입니다. 완전구현, 이진트리, BFS, DFS 정도면 충분합니다. 이것도 사실 대기업 기준... 하지만 파이썬을 구현하고 더 깊게 팔수록 회사에서 기회가 더 많이 주어진다는건 명확해요. 요즘같은 LLM이 assist를 해주는 요즘 분석가와 같은 semi- IT직군이 LLM을 등에 타고 훨훨 날아가는 건 시간문제이니까요~ 잘 쓰려면? 잘 이해해야한다.말이 길었네요~ 여튼 대부분 알고리즘을 포기하는 지금까지 파이썬 문제를 잡고있다는건 노력도 많이하시고 열심히 하고자는 배경이 있다는 것이니까 꼭꼭 이점을 장점으로 가져가서 엔지니어링 스택도 가져가는 희귀한 데이터분석가가 되셧으면 좋겠습니다. 혹 또 궁금한거 있으면 찾아오세요~
3. 위 대답을 하게 된 이유
3.1. 시대적 배경
회사에서 나와서 좀 더 넓은 시야로 데이터 분야를 보다보니 직군에 대한 이해가 명확해졌다. 사실 데이터 분석가라는 직군은 정의된지 10년도 채 되지 않아서, 테크기업은 활발히 채용하고 있지만 대기업의 경우 이제 데이터 팀이 만들어지고 데이터 리터러시 문화가 도입되는 중이다.
이런 흐름에서 사실 데이터 직군은 높으신 분들에서는 그저 IT직군 중 하나이다. 마치 병원에 제약사에 누가 방문해도 영원사원1, 2 처럼 의사 선생님이 생각하시는 것 처럼.. 이런 상황에서 데이터 팀은 자신의 specialty는 추구미로 유지하되, 회사와 시대가 요구하는 사항에 유연하게 pivot 할 필요가 있다. 다시 말해 "저 분석 만 할거에요!"라고 자신의 기능을 제한하기 보다는 "데이터로 볼 수 있는 문제를 해결하는 사람"으로 추구미를 갖는 것이 장기적으로 유익하다. 그런 관점에서 데이터 잡부라고 속칭 자책하는 분들은 어찌보면 다양성을 갖춰가는 과정일 수 있다.
그렇다면 "데이터로 문제를 해결하는 사람이"되기 위해서는 기능과 도구에 국한하면 안된다. Python으로 할 수 있는 것들이 굉장히 다양하므로 웹 프로그래밍, 자동화, 분석, ML, 통계, CS 등 하나씩 즐겨찾기를 해놓는 것이 좋다. 나는 이 관점을 가지게 된 이유가 LG 데이터사이언스 캠프로 튜터로 일했던 2023년이고 지금도 그 방향을 향해 하나씩 분할 정복해가고 있다. 당시 공부방향을 작성해놓았다.
1. 변화의 배경 과거: 2020년 팬데믹 시기에 기업들은 너도나도 데이터 과학자를 고용했지만, 이들 중 상당수가 이론에만 강하고 실제 서비스에 모델을 적용하지 못하는 문제를 겪었습니다. 현재 기업들은 이론적인 모델이 아닌, 실제 문제를 해결하고 비즈니스에 가치를 창출하는 실용적인 솔루션을 원하게 되었습니다. 이로 인해 많은 데이터 과학자들이 해고되었고, 채용 기준도 바뀌었습니다.
2. 학위의 한계와 요구되는 기술 학위의 문제점: 기존 데이터 과학 학위 과정은 확률 분포나 통계적 추론 같은 이론에 치우쳐 있어, 실제 현장에서 필요한 코딩이나 모델 배포 등의 기술을 제대로 가르치지 못합니다.
기업이 원하는 기술: 기업들은 데이터 파이프라인 구축, 클라우드 인프라(AWS, Azure 등), 컨테이너(Docker, Kubernetes), API 개발 등 실무에 바로 투입될 수 있는 기술을 가진 전문가를 찾고 있습니다. 특히, AI와 자연어처리(NLP) 관련 기술에 대한 수요가 급증하고 있습니다.
3. '만능 데이터 과학자' 시대의 종말 전문성의 중요성: 이제 기업들은 모든 것을 아는 만능형 데이터 과학자 대신, 특정 분야에 특화된 전문가를 원합니다. 머신러닝 엔지니어, 데이터 엔지니어, AI/ML 전문가, 분석 엔지니어 등으로 역할이 세분화되고 있습니다.
4. 살아남기 위한 전략 데이터 과학 분야에서 성공하려면 학위만으로는 부족합니다. - 실제 배포 경험 쌓기: Docker, 클라우드 플랫폼(AWS, Azure 등), API 배포 등을 직접 경험해야 합니다. - 데이터 엔지니어링 기본기 다지기: SQL, 데이터 웨어하우징, Apache Spark 등 데이터 처리 기술을 익혀야 합니다. - 비즈니스 문제 해결에 집중: 기술을 비즈니스 문제 해결에 적용하고, 비전문가에게 결과를 명확히 설명하는 능력을 길러야 합니다. - 특화된 분야 선택: LLM/NLP, 컴퓨터 비전, MLOps 등 수요가 높은 분야를 선택해 전문성을 키워야 합니다.
결론적으로, 데이터 과학 분야는 여전히 유망하지만, 이론적 지식에만 머물지 않고 실제 문제를 해결할 수 있는 실무 역량을 갖춘 인재를 필요로 하는 방향으로 진화하고 있습니다.
3.3. 그래서 알고리즘을 알아야하는가? 라는 질문에는
상황에 따르지만 나는 "한번쯤 공부해볼 만하다" 라고 대답한다. 우리의 세부 전공은 회사와 시대에 따라 달라질 수 있고, 그 기반이 되는 것이 튼튼한 기초 지식이다. 사실 이건 알고리즘에 국한하는 것이 아니라 CS, 통계, 클라우드 등 데이터로 문제를 해결하기 위한 앞뒤의 기반들을 포괄한다. 종합적인 관점에서 만약, 구직/이직하는 회사가 코테를 요구하면 최소한의 '완전 구현' 유형이라도 이직 준비할겸 해보길 바란다. 대기업과 같은 일반적인 역량을 요구하는 곳일 수록 더 알고리즘 테스트를 요구할 확률이 높다. 왜냐? 대기업은 한 명을 뽑아서 장기적으로 사용하는 것을 고민하기 때문이다. 일반적인 역량이 있는 사람을 좋아한다. 또한, 본인이 장기적으로 데이터 직군의 과도기를 거쳐 매니저 레벨까지 가고 싶으면 해보아라. 기본기에 가까운 CS와 같은 내용이 좋다. 나는 이런 책들을 하나씩 읽고 기록해나고 있다.
한국농수산물유통공사에서는 kamis라는 데이터 정보 공개 시스템이 있습니다. 이따금 실습용으로 데이터를 불러와서 과일, 채소 시세를 만들어보는 목적으로 데이터를 수집해보는 편입니다. 과거에는 직접 python 코드로 작성해서 데이터를 불러왔는데, 서버를 띄우지 않아서 파이프라인까지는 완성하지 못했습니다. 이번에는 n8n을 공부하면서 low code를 no code 툴로 바꿔서 수집해보고 그 차이에 대해서 한번 고찰해봅니다.
위 분석은 사실 n년전 사이드 프로젝트를 하면서 구현한 결과입니다. 장바구니 대시보드라는 이 주제는 사실 지속성있게 데이터를 수집해야만 의미를 가집니다. 하지만 당시 로컬에서 retrospective 그러니까 현재 기준 과거 날짜에 대한 데이터만 수집했습니다. 지속가능한 프로덕트를 위해서는 서버에 올려서 자동화 및 파이프라인을 구축해합니다. 그래야 대시보드의 본질을 찾을 수 있기 때문입니다.
하지만 당시 이리저리 우선 순위에 밀려 단적인 그래프를 그리고 마치게됩니다. 클라우드에 서버를 빌리고 수집하는 과정이 어려운 일은 아니지만 번거로웠기 때문입니다. 그렇게 시간이 지나 월간 데이터 노트라는 스터디에서 다시 한번 부활하게 되었습니다. n8n이라는 자동화 도구를 이용하면 좀 더 편하게 데이터를 수집할 수 있을까요?
2. 결과: workflow, 데이터
Google Sheet에서 데이터를 주고 받으려면 n8n 클라우드에서는 노드만 추가하고 구글 로그인을 하면 됩니다. 하지만 셀프 호스팅의 경우 인증을 위한 사전 절차가 필요합니다. 다음 n8n 문서에 잘 나와있어서 따라하기만 하면 됩니다.
기본적으로 n8n은 오픈소스여서 내 컴퓨터에 설치해서 실행할 수 있습니다. 하지만 이 과정이 조금 복잡하기 때문에 14일간의 무료 trial 기간을 제공합니다. 셀프호스팅을 하지 않고 진행할 수 있는 점이 장점입니다. 로컬에서 수집하려면 파이썬 설치, 가상환경 세팅 등이 번거롭습니다. 하지만 n8n 은 node로 이루어진 구성 요소를 선택하고 연결함으로써 쉽게 원하는 목적을 달성할 수 있습니다.
장점2: 시각화, 쉬운 모듈화
이따금 복잡한 서비스를 만들다 보면 코드레벨에 있는 스키마를 보여주기 위하여 figjam과 같은 3rd party tool이용해서 그려야합니다. 하지만 n8n은 그 자체로 로직이 보이기 때문에 굳이 스키마를 만들기 위한 시간과 노력을 기울이지 않아도 됩니다. 또한 노드들 하나 하나의 input ouput이 명확하게 보여서 어떻게 작동하는지 파악하기 쉬웠습니다.
Input -> 개별 노드의 파라미터 -> Output
장점3: 좋은 레퍼런스들
입문자들에게는 n8n에서 제공해주는 혹은 강의나 블로그에서 제공해주는 .json형식의 workflow를 따라하는 것만으로도 도움이 됩니다. 공식 문서는 제가 근래 본 오픈 소스 중에 가장 이해하기 편했으며, 포럼도 잘 활성화 되어있습니다. 서비스 안에서는 사이드바에서 LLM assist를 받을 수 있습니다. 생각해보니 LLM assist는 클라우드에서만 제공되는 단점이 있네요.
(좌) Docs (우) Forumworkflow 화면 사이드에 chatbot이 존재
단점1: 어쨋든 알아하는 작동 방식
노코드 툴이 가지는 전체 문제점이기도 하나, 각 개별 노드가 작동하는 방식을 어렴풋히라도 인지 하지 않으면 설계 자체가 불가능합니다. HTTP 노드에서 수행하는 POST/GET 요청의 차이 받아 처리하는 JSON 자료형, API의 개요, 구글 시트를 연결하기 위한 인증 과정들에 대한 이해가 있거나 해결하려는 마음이 있어야 원하는 결과물을 얻을 수 있습니다. LLM에서 "해줘"라고 요청했을 때 바로 수행하는 그런 경험을 기대했다면 실망할 수 있습니다.
단점2: 셀프호스팅의 할 경우 올라가는 러닝커브
n8n을 최초 회원가입하면 14일간의 무료 Trial을 주지만 결국 끝나면 유료 결재를 하게 될 것입니다. 비용을 피하고 싶다면 셀프 호스팅을 해야하는데 docker등에 대한 도구를 사용하므로 자연스럽게 러닝커브가 올라갑니다. 개발에 익숙하면 어렵겠지만 결국 노코드의 장점을 일부 잃어버린다고 할 수 있겠습니다.
4. 짤막한 후기
로우 코드로 직접 구현하고 만드는 과정이 꽤나 귀찮았는데 n8n으로 하니 파이프라인도 쉽게 만들 수 있는 장점이 있는 것 같습니다. 비개발자를 위한 툴이기도 하지만 개발자들도 쉽게 만들 수 있는 툴인 것 같고 Make, Zaiper 등이 가지고 있는 자동화 라인에서 굳건히 자리를 지킬 것 같네요! 종종 쓰게 될 것 같습니다.
아미노산 서열을 가지고 핵에 붙는 단백질인 NLS Explorer로 만들어지는지 예측하는 모델로 Attention 기법을 이용한 딥러닝 분류 모델을 구현해보겠다.
1. 배경지식
핵 위치 신호(NLS)는 단백질 내 중요한 아미노산 서열로, 세포핵으로의 이동을 조절합니다. 이러한 신호를 매개로 한 핵으로의 진입은 핵 수송에 관여하는 필수적인 생물학적 수입 과정입니다. 단백질에서 이러한 부분을 추가하거나 분리하면 핵 위치 신호에 상당한 변화가 나타날 수 있습니다. 이러한 신호 펩타이드에 대한 심층적인 탐구와 위치 분석은 핵 수송의 복잡성을 밝힐 뿐만 아니라 이러한 생물학적 현상을 지배하는 기본 원리를 밝히는 열쇠를 쥐고 있습니다.
아미노산 서열이 접혀서 단백질이 되는 과정을 재현하려면 직접 실험으로 환경을 구축해해야하는데 노동집약적이라 딥러닝으로 분류하게다는 그런 의미이다.
# Mac OS M4(Silicon) Pro
conda create -n NLSExplorer-pyto python=3.7 --platform osx-64
#콘다 환경 설정
conda activate NLSExplorer-pyto
#OS 설정
conda env config vars set CONDA_SUBDIR=osx-64
#패키지 설정
conda install pytorch torchvision torchaudio cudatoolkit -c pytorch -c nvidia
맥북에서는 CUDA가 작동하지 않으므로 Pytorch를 사용하려면 맥에서 자체개발한 MPS 활성화 체크를 하고 사용
import torch
if torch.backends.mps.is_available():
mps_device = torch.device("mps")
x = torch.ones(1, device=mps_device)
print(x)
print("PyTorch is using MPS (Metal Performance Shaders) for acceleration.")
else:
print("MPS is not available. PyTorch will run on CPU.")
print(f"PyTorch version: {torch.__version__}")
Dataset: Put NLSExplorer-p in the folder named "./A2KA_train/Dataset" and extract it using appropriate commands (e.g.,unzip xxx).
3. 코드 작동 뜯어보기
웹사이트 구현도 해놓음!
다음 3.1 ~ 3.3은 Gemini CLI Pro를 이용하여 분석한 핵심 기능 정리
3.1. 핵심 기능
A2KA: 단백질 언어 모델의 표현을 개선하고 어텐션 메커니즘을 활용하여 예측에 중요한 영역을 식별하는어텐션 네트워크
SCNLS: 서열의 패턴 분석, 특히 NLS와 같은 불연속적인 패턴 분석을 위해 설계
NLS (핵위치신호) 예측: 딥러닝 모델(A2KA)을 사용하여 단백질 서열에서 NLS를 예측합니다.
패턴 분석: SCNLS 알고리즘을 사용하여 단백질 서열, 특히 NLS에서 연속적이지 않은 패턴을 분석합니다.
3.2. 주요 파일 및 역할
A2KA.py: 어텐션 메커니즘을 사용하는 딥러닝 모델인 A2KA의 PyTorch 구현을 포함합니다. 이 모델은 단백질 서열 임베딩을 입력으로 받아 NLS 존재를 예측하고 어텐션 분포를 출력합니다.
SCNLS.py: NLS와 같은 서열 모티프에서 불연속적인 패턴을 검색하고 분석하는 SCNLS 알고리즘을 구현합니다. 여러 모드(파일, 단일 서열 등)로 실행할 수 있으며, 멀티프로세싱을 활용하여 효율성을 높입니다.
utils.py: 데이터 로딩, 전처리, 모델 학습, 임베딩 생성 등 다양한 유틸리티 기능을 포함합니다. 특히 esm 라이브러리를 사용하여 단백질 서열의 임베딩을 생성하는 기능이 중요해 보입니다.
Plot.py: 3D 막대 그래프를 생성하여 다차원 데이터를 시각화하는 스크립트입니다. 모델의 어텐션 가중치나 다른 분석 결과를 시각화하는 데 사용될 수 있습니다.
test.py: 텍스트 데이터에서 연속적인 패턴을 추출하고 빈도를 계산하는 테스트 코드를 포함합니다.SCNLS.py의 연속 모드와 유사한 기능을 테스트하는 것으로 보입니다.
3.3. 작동 흐름
데이터 준비: utils.py의 함수를 사용하여 단백질 서열 데이터를 로드하고 전처리합니다. example.csv 또는 Arabidopsis thaliana_0.5.pkl과 같은 데이터 파일을 사용합니다.
임베딩 생성: utils.py의 generate_representation 함수를 사용하여 esm 모델로 단백질 서열을 임베딩 벡터로 변환합니다.
NLS 예측 (A2KA): A2KA.py에 정의된 A2KA 모델을 불러옵니다. 생성된 임베딩을 모델에 입력하여 NLS 존재 여부를 예측하고, 모델이 서열의 어느 부분에 주목했는지 나타내는 어텐션 맵을 얻습니다.
패턴 분석 (SCNLS)
SCNLS.py를 사용하여 NLS 서열 또는 다른 관심 서열에서 패턴을 분석합니다.
--mode 인자를 통해 파일(f), 단일 서열(s), 또는 미리 처리된 데이터(n)를 입력으로 지정할 수 있습니다.
--maxgap, --kths 등의 파라미터를 조절하여 패턴 분석의 세부 설정을 변경할 수 있습니다.
5. 결과 시각화: Plot.py를 사용하여 분석 결과(예: 어텐션 가중치)를 3D 그래프로 시각화할 수 있습니다.
3.4. 요약
이 리포지토리는 NLSExplorer라는 딥러닝 기반의 NLS 예측 및 분석 도구를 제공합니다. A2KA 모델을 통해 NLS를 예측하고 해석 가능한 어텐션 정보를 제공하며, SCNLS 알고리즘으로 서열 패턴을 심층적으로 분석할 수 있습니다. utils.py는 데이터 처리 및 임베딩 생성을 지원하고, Plot.py는 결과 시각화를 돕습니다.
이번 장에서는 캐시에 대해서 기술하며 책의 5번째 여정입니다. 다시 복기하자면 컴파일러 -> 프로세스/스레드 -> 메모리 -> CPU에 이은 장이며, 컴퓨터가 캐싱을 하기 위한 방법과 문제에 대해서 서술합니다.
1. 캐시, 어디에나 존재하는 것
CPU는 메모리에서 정보를 가져와 업무를 수행하며, 이를 더 효율적으로 수행하기 위해서 캐시를 발명
도서관으로 비유하면 캐시는 책상 메모리는 서고
캐시는 L1,L2,L3로 나눠어져 있으며 점차 그 크기가 커짐
가상메모리의 경우 실제 메모리보다 더 크게 설정될 수 있는데 이 경우 하드디스크에 데이터를 저장
저장 형태의 계층 구조
맥북의 경우 활성 상태 보기에서 캐시에 대한 정보를 확인할 수 있음
하단 Cached Files
이전에 인텔 맥을 사용하면서 16기가 중에 거진 15기가를 점유하고 사용하고 있었다. 맥북을 새로 구입하면서 48기가로 구입했는데 오히려 사용량이 31기가로 늘었다(거의 비슷한 환경 구성임에도) 이로 미루어 볼때, 이전에 메모리가 부족해서 로컬 스토리지(SSD)에서 끌어다 쓰고 있다는 사실을 알게되었다.
궁금증1) 파이썬 데이터 처리 메소드 pd.read_csv는 메모리에 저장할까 캐시에 저장할까?
메모리에 저장한다. `info()`, `memory_usage()`로 확인 가능하다
궁금증2) 최근에 채점 서비스를 개발하면서 streamlit 프레임워크(파이썬 프론트엔드)에서 데이터를 저장하는 것을 캐싱으로 제안받았는데 이게 뭘까?
이번 글은 NVIDIA의 certificate 중 하나인 Prompt Engeering 자격증에 대한 취득 과정을 기술합니다. 또한, 학습과정에서 NotebookLM, GPTs, 노션AI를 활용해서 효과적으로 학습한 후기를 함께 남깁니다. 추후 다른 자격증을 공부하실 때 활용할 수 있는 좋은 레퍼런스가 될 수 있길 바랍니다.
인증 자격 대상이 된다면 다음 링크에 지원서를 작성합니다. Application 링크 저는 소속 기업이 있어서 스폰을 받아 지원했고, experience와 delivery plan을 집중적으로 적는게 권장됩니다. 내가 어떤 경험이 있고 이 자격을 획득하여 어떻게 사용할 것인지 기술하는 과정입니다. 정상적으로 접수되면 수 일 내 등록된 이메일로 연락이 오며, 보통 Monthly하게 학습 코호트가 시작되어 저는 바로 6월에 참여할 수 있었습니다. 해당 참여 코호트 안에 교육 이수와 최종 인터뷰까지 하는 것이 필수적으로 요구됩니다.
Student Assesment: 자체적으로 운영하는 Jupyter notebook 플랫폼에서 실습를 하는 과정입니다. 2주간 소요됩니다.
Review Instructor video: (선택사항) Assesment를 이수하면 제공되는 해설 강의입니다.
Certification Interview: 최종 통과를 위한 화상 면접(영어)이 진행됩니다.
2. 이수 진행
2.1. Student Assement 개요
Student Assessment가 가장 투자 시간을 많이 하는 부분입니다. 기본적인 프롬프트 엔지니어링 기술과 함께 LCEL, Pydantic, Gradio 와 같은 프레임 워크를 알려줍니다. 하지만 정작 평가는 간단히 자연어로 프롬프팅하면 통과가 되는 쉬운 문제로 구성되어 있습니다.
교안 자체는 Jupyter notebook으로 코드가 작성되어 실행만하면 대부분 원하는 결과를 얻을 수 있지만 이 학습 방식이 생각보다 굉장히 지루했습니다. 사실 영어라서 눈에 잘 안들어오기도 하고, 이미 작성된 코드 + 추상화된 Langchain 코드의 특성상 안에 들어가는 원리를 파악하는데는 큰 도움이 되지 않았습니다. 서버에서 이미 패키징된 NVIDIA api를 사용해서 실습하는 방식인데, 저는 로컬에서 직접하고 싶어서 Container에 가입하고 했는데, 세상에 NVIDIA에 한국 verification이 없습니다 :< 통신사 이슈인 것 같은데, 그래서 그냥 gemini로 변경해서 진행했습니다.
API key를 얻기 위해서 Verification 필요
그 과정에서 Langchain의 공식 Docs는 물론 Teddynote님이 작성하신 랭체인노트도 참고를 했습니다. 프롬프트 엔지니어링부분만 본다면 사실 많은 클래스와 메소드를 사용하지는 않지만, 워낙 Langchain이 클래스와 그 클래스를 상속한 다른 클래스가 많아서 가독성이 좀 좋진 않았습니다.
2.2. 학습 방법: NotebookLM
추가적으로 이번에 NotebookLM을 사용해봤는데, 학습에 필요한 링크나 자료(pdf)등을 업로드 하면 RAG를 자체적으로 구축하여 질의응답을 할 수 있는 시스템입니다. 다 넣어놓고 핵심 부분을 뽑아달라고 하거나 아니면 개인적으로 궁금한 것들을 대화형으로 공부하니까 훨씬 집중이 잘되었습니다. 개인적으로 진짜 학습하기 좋은 환경인 것 같네요.
자료가 올라간 NotebookLM
3. 면접
3.1. 영어 면접 준비 - GPTs
Assessment는 충분한 시간을 가지고 문제를 풀 수 있지만 문제는 인터뷰 입니다. 교육에 관련하여 일정이나 Timeline을 알려주는 DLI 담당자가 미국에 있어서 새벽에 볼 줄 알았는데 다행이 대만에 Principle Instructor가 있어서 주간 시간 내에 볼 수 있었습니다. 프롬프트 엔지니어링과 그 기반의 이론들은 한국어로 설명하려면 어렵진 않지만 영어로 표현이 잘 안되어서 GPTs에 자연어로 빌드해놓고 공부했습니다. 확실히 다른 LLM과 달리 ChatGPT에서 지원하는 GPTs 만큼은 음성으로 대화도 편리하게 잘되고 LLM에 대한 프로토 타이핑 관점에서 유용하게 쓸만한 것 같아서 여전히 ChatGPT가 매력적인 포지션을 가지고 있는 것 같습니다. 또 면접 시작 전 30분 정도는 ChatGPT 어플과 영어로 대화하면서 입을 풀었습니다.
3.2. 면접 과정
프롬프트 엔지니어링의 수행 방법은 간단하고 쉽지만 그 기반에 대한 질문들이 많이 나왔습니다. 처음부터 토크나이징, 벡터화, one-hot encoding 의 정의와 차이점을 기술하라 등 부터 시작하여, Runnable, Tempature와 같은 Langchain와 LLM에 관련된 핵심 개념들을 질의응답 했습니다. 생각보다 Langchain의 핵심 메소드나 클래스에 대한 질문은 덜 했고 이론을 위주로 티키타카하는 시간이였습니다. 30분간 진행해서 처음에는 집중력이 좋았지만, 후에 갈수록 질문을 잘 못듣고 놓치는 상황이 발생해서 조금 아쉬웠습니다.
3.3. 면접 후 정리: 노션 AI
요즘 Notion에서 노트AI라고 회의 내용을 저장하고 요약해주는 기능을 제공합니다. 면접과정에 이걸 켜놓고 복기하기 위한 수단으로 사용했습니다. 처음 당시는 무료로 시간제한도 없고 실시간 streaming하게 적어나가는 모습이 보였는데, 서버 부하 때문인지 녹음 시간이 제한이 되기 시작했습니다. 그래도 접근성도 좋고 편의성도 좋은 툴이라 복기할 때 잘 썼습니다.
4. 후기
미루고 미루던 Langchain을 접할 기회가 있어서 좋았습니다. 일과 병행하느라 처음에는 적잖이 집중을 못했는데 다양한 AI툴과 나의 학습방법을 갖춰나가니 조금 더 정리하는 시간이 되어서 매우 좋았습니다. 관련하여 더 괜찮은 자격증이 있으면 추후 학습 기회로 삼아볼 생각입니다. 프롬프트 엔지니어링에 대한 공부 내용과 레퍼런스는 다음 글에 작성해놓았으니 참고하시길 바랍니다!
이번 글에서는 NVIDA DLI Course에서 수행한 과정에서 LLMs 특성과 그를 보완하기 위한 방법에 대해서 작성합니다. 프롬프트엔지니어링이 무엇인지 그리고 Langchian으로 어떤 클래스와 메소드를 이용해서 구현하는지 알아보겠습니다.
1. LLM이 무엇인가?
LLM은 2017년 Google 발표한 Transformer를 기반으로하는 딥러닝 아키텍처. 핵심은 문맥을 이해하기 위해 단어 간의 관계를 계산하여 토크나이징과 벡터화를 이용하여 정보를 추상화
딥러닝은 행렬과 가중치로 데이터를 저장. 음악 믹서로 비교하자면, 보컬,드럼,베이스에 대한 소리를 조절하는 슬라이더가 있고, 그 슬라이더의 위치(가중치)를 바꾸면서 음악을 만들어내는 것과 같음. 수십개의 슬라이더의 조합이 행렬이라고 할 수 있음
이 가중치는 사전 학습(Pretrain)때 정해지므로 내부 규칙이자 고정된 노하우. 내가 프롬프트를 한다고 해서 이 고정된 규칙이 변하는게 아니라 context window가 계속 추가하고 변화되기 때문에 결과값이 변하는 것처럼 느껴지는 것
LLM의 특징
장점: 강력한 기능과 광범위한 활용성
단점: 할루시네이션에 대한 문제
2. LLM의 단점의 보완 대책
프롬프트 엔지니어링: 원하는 응답을 얻기 위해 효과적으로 소통하는 방식
Chain of Thought: LLM이 복잡한 문제를 중간 단계로 분해하여 생각하도록 유도하는 복잡한 추론 능력을 향상시킴. 명시적인 예시 / 단계별로 생각을 유도 / 작업 과정을 보여주게 하여 보완
검색 증강 생성(RAG): LLM의 지식 한계를 보완하기 위해 외부 지식과 LLM을 결합하는 기술, 문맥적으로 관련성 높은 응답을 제공. 이 때 코사인 유사도를 통해 의미 유사도를 벡터 검색함
가드레일: 신뢰있는 LLM을 구축하기 위한 제약조건과 규칙. 부적절한/유해한 응답을 방지
3. What is Langchain?
LangChain은 이러한 기반 어플리케이션 개발을 간소화하고 유연하게 만드는오픈소스 프레임워크. 상당한 추상화를 제공하여 초보자도 쉽게 접근할 수 있는 장점이 있으나, 지나친 추상화로 인한 디버깅이 힘든 단점이 존재하기도 함
핵심 개념
Runnable: LLM의 인스턴스로, 기본 작업 단위. RunnableLambda를 이용해 사용자정의함수를 Runnable로 변환 가능
LCEL: LangChainExpressionLanguage Runnable을 체인으로 구성하는 선언적인 방ㅇ식 제공
표준화된 메소드: invoke, batch, stream 표준 호출 메소드를 제공합니다.
프롬프트 엔지니어링 기법 지원
역할 기반 메시지: ChatPromptTemplate.from_messages 메소드를 사용하여 Human, AI, System 와 같은 역할 기반 메시지를 명시적으로 정의
퓨삿 프롬프팅: FewShotChatMessagePromptTemplate 모델 동작에 영향에 미치는 예시를 사전에 제공할 수 있음
프롬프트 엔지니어링 관련 Lanchain 디렉토리
#langchain_core
runnables.base # Runnable
├── Runnable : 기본 단위
├── RunnableLambda: 함수를 Runnable로 변환
├── RunnableParallel:
│
langchain_core.prompts.chat
├── ChatPromptTemplate
│ ├── from_messages(): reate a chat prompt template from a variety of message formats.
│ # 튜플 형식, "SystemMessage" 등의 키워드 사용 가능
├── few_shot.FewShotPromptTemplate: fewshot 제공
│
output_parsers.string
├──StrOutputParser: llm이 반환하는 결과를 문자형으로 변환
#llm api class
langchain_google_genai
├──ChatGoogleGenerativeAI
│ ├──invoke()
│ ├──stream()
│ ├──batch()
4. 용어집(Glossary)
대분류
소분류
설명
Prompt Engineering
Prompt Engineering
LLM의 가진 본질적인 특성 할루시네이션을 제어하고 원하는 결과를 도출하기 위한 방방법
Prompting
LLM에게 작업을 지시하는 행위 그 자체
Prompt Template
프롬프트의 구조를 미리 만들어 놓고 변수를 주입항여 재사용성을 높이는 도구
System Messages and System Context
모델의 역할, 성격, 행동 지침을 설정하는 시스템 레벨의 지시어
Chain-of-Thought
모델이 복잡한 문제를 풀 때, 생각 과정을 단계별로 서술하게 항여 정확도를 높이는 프롬프트 기법
Few-shot Classification
몇 가지 예시(few-shot)을 프롬프트에 포함하여 모델이 특정 패턴이나 분류 기준을 학습하게 하는 기법
Prompt Injection
악의적인 사용자가 프롬프트를 조작하여 개발자가 의도하지 않은 동작을 유도하는 보안 공격
개발 프레임워크 및 도구
LangChain
LLM을 외부 데이터 및 다른 서비스와 쉽게 연동하여 어플리케이션을 만들도록 돕는 개발 프레임워크
LCEL
Langchain의 여러 컴포넌트(Runnable)을 파이프라인으로 직관적으로 연결할 수 있게 하는 선언형 문법
Runnables
LCEL을 구성하는 기본 단위
Gradio and Chatbots
머신러닝 모델의 UI를 빠르게 만들어 프로토 타이핑하고 공유할 수 있는 도구
Pydantic
Python에서 데이터 유효성검사와 구조화를 쉽게 해주는 라이브러리
고급 어플리케이션 패턴
Agents
LLM과 RAG를 포함한 다양한 기술(플러그인, API)을 활용하여 사용자의 질문을 처리하고 실제 행동을 수행할 수 있는 인공지능 시스템
Retrieval Augmented Generation (RAG)
LLM의 추상화된 벡터에 의존하지 않고 외부 데이터베이스에서 정보를 검색하여 답변을 생성(Generation)하는 기술
Structured Data Generation
LLM이 단순 텍스트가 아닌 JSON/XML 등 정해진 형식의 구조화된 데이터를 생성하도록 하는 작업
LLM 모델 및 동작 제어
SOTA LLM models (ChatGPT, LlamaX, etc.)
현재 최고 성능을 보이는 모델들
Temperature
모델 답변 생성시 무작위성을 조절하는 파라미터, 높을수록 창의적
Instruction fine tuning
특정 작업이나 스타일에 맞게 응답하도록 사전 학습된 LLM을 명령어 데이터셋으로 학습시키는 과정. zero-shot에도 잘 작동할 수 있게됨
Hallucinations
다음 단어를 예측하는 LLM의 특징 때문에 일어나는 현상이며, 실제로 사실이 아닌데 사실 인 것처럼 말하는 현상
데이터 처리 및 인프라
JSON
JavaScript Object Notion.key-value 쌍으로 이루어진 데이터 교환 포맷.
Streaming vs. Batching
스트리밍은 결과가 생성되는 즉시 실시간으로 전송하는 방식, 배치는 한 번에 처리하는 방식
Nvidia NIM
NVIDIA Inference Microservice, 쿠버네틱스 기반 마이크로서비스 세트
자체적으로 채점자동화를 위한 웹 서비스를 만든 후기를 작성해봅니다. 소규모 인원을 대상으로 GCP 서비스를 조합해서 편하게 채점할 수 있도록 만들었고 그 과정에서 개발, 배포, 서비스 운영에 대한 전반적인 내용을 다룹니다. 그 과정에서 일어난 시행 착오를 공유하여 독자들이 서버리스 자동화를 설계할 때 사전에 고려해야할 점 등을 예습하는 글이 되길 바랍니다.
1. 채점 서비스의 필요성
1.1. 기존 채점 프로세스의 문제점
문제1) 과제 제출 -> 피드백 까지의 시간 지연
기존에는 학생들이 과제를 제출하면 이를 받아 채점 튜터들이 채점하고 리뷰를 하는 구조로 진행 되어있습니다.
학생들의 답안 제출 -> 교육 시스템에 적재 -> 튜터에게 할당(10명 등) -> 각자의 로컬 컴퓨터에서 코드를 실행 & 리뷰를 작성
답안 제출로부터 학생들이 피드백을 받을 때까지 최소 1주 - 최대 2주의 시간이 소요되었습니다. 문제를 푸는데 이렇게 시간이 오래 걸린다면 코드를 작성할때의 기억들이 휘발되어 학습효과가 떨어집니다. 따라서 위 프로세스 중에 채점하는 시간을 줄이고자 했습니다.
문제2) 환경 의존성 문제
데이터 분석을 포함한 프로그래밍은 환경 설정에 따라서 그 결과값의 차이가 있을 수 있습니다. 예컨데 SQL로 데이터를 불러온다고 해도 각자 데이터를 불러오는 DDL/DML이 다르거나 환경이 다르면 다른 결과물을 산출할 수 있습니다. 과제를 풀 때나, 채점을 할 때나 각자의 로컬 환경에 MySQL-DBeaver로 설치를 하는 환경이 문제였습니다.
따라서, 위 문제를 해결하기 위해서 하나의 서비스를 만들게 되었습니다. 제출된 코드에 대한 결과는 채점 자동화는 물론, 코드에 대한 정성적 피드백도 Gemini로 받았으면 좋겠다고 생각했습니다.
2. 채점 시스템 설계
2.1. 기존 프로세스 개선
사실 서비스를 시작하기에 앞서 이 서비스를 전체적으로 알리고 프로세스를 개선하는 필요성이 있다고 생각 했습니다. 따라서 과제를 제출하는 과정부터 프로세스를 개선하기 위해서 교육업체 담당자에게 설명하기 시작했습니다.
보통 과제가 나가는 과정은 튜터의 과제 제작 -> 학생들이 풀이 -> 튜터들의 과제 채점 -> 결과 송부 의 형식으로 진행됩니다. 일관된 채점을 위해서는 먼저 과제 제작에 대한 설계도 필요했습니다. 그래서 과제를 제출할 시 몇가지 기준을 세웠습니다.
과제에 대한 의도, 목적 등을 작성할 것
과제에 채점 기준을 명시할 것
모범 답안으로 해설지를 작성할 것(정답 코드 포함)
이렇게 되면 일관된 과제 제출도 가능해지고 추후 채점을 Gemini에 요청할 때 Prompt 의 기반이 될 것이므로 이 형식을 같이 잡아나갔습니다.
2.2. 채점 서비스 설계
위 업무 프로세스 개선과 동시에 서비스 설계를 위한 서비스와 스택을 정리해나갔습니다. 가장 중점적인 것은 GCP의 on-demand 서비스인 Cloud run으로 서버리스 구조를 선택하게 되었습니다. 서버를 관리하고 올리기엔 관리포인트가 클 것이고 간단하게 만들 수 있을 것 같아 선택했습니다.(하지만 이 선택을 후회하게됩니다) 웹사이트는 Streamlit으로 정했고, . 또한 과제는 크게 SQL와 Python으로 나뉘는데 SQL의 경우 실제 MySQL 8.0 버전이 필요해서 GCP의 Cloud SQL을 사용하였습니다. 파이썬의 경우 내장함수으로 exec 를 이용해서 채점 기능을 구현했습니다.
과제는 확장될 수 있지만 기본적으로 SQL, Python, Pandas에 대한 문제를 기본으로 결과를 만들어 갔습니다. 크게 기능은 모범 정답과 학생 정답을 채점하는 기능 1번과 정성 평가를 하는 기능2가 있습니다.
기능 1) Gemini를 이용한 정성평가( 코드의 효율성과 개선점을)을 함께 평가
사전에 선정해놓은 기준으로 Prompt engineering에 따라 점수를 측정
기능 2) 학생들의 답과 모범 정답 비교
SQL) Cloud상의 SQL 서비스에 직접 쿼리를 날려 가져와 출력
Python) exec 내장함수를 이용해서 테스트 케이스마다의 출력
Pandas) 코드저장소의 csv파일을 불러와 데이터 가공한 결과를 출력
3. 핵심 기능 구현 상세
기능1) 정성 평가
구현 내용
정성 평가는 몇가지 기준에 따라 채점됩니다. 2.1. 기존 프로세스 변경 제안에서 말한 것처럼 과제에 대한 의도, 채점 기준, 모범 답안 등의 대한 정보와 사전 프롬프트를 받아 결과를 뱉어냅니다. 다음은 `prompt_builder.py` 의 결과물이며 Langchain의 from_template 메소드처럼 사전에 변경 가능한 구조로 설계하여 확장정을 높였습니다.
당신은 {category} 과제 채점 전문가입니다.
- 문제: {question}
- 모범답안: {model_answer}
- 학생답안: {student_answer}
- 평가기준: {evaluation_criteria}
- 쿼리 상태: {query_status}
{grading_criteria_str}
학생의 답안이 모범답안과 다르더라도, 논리적/문법적 오류가 없다면 높은 점수를 주세요.
아래 조건을 꼭 지켜서 평가해 주세요.
1. 각 평가기준(정확도, 가독성, 효율성, 분석력 등)에 대해 **5점 단위(0, 5, 10, ..., 100)로만** 부분 점수를 각각 매겨주세요. (예: 정확도 50점, 가독성 20점, 효율성 15점)
2. 각 기준별 점수의 합이 100점이 되도록 최종 점수도 함께 출력해 주세요.
3. 각 평가기준별로 '참고할 점'을 한두 문장으로 구체적으로 작성해 주세요. (예: 잘한 점, 부족한 점, 개선점 등)
4. 마지막 피드백은 평가자가 참고할 수 있도록, 학생에게 직접 전달하는 친근한 말투가 아니라 틀린 부분, 부족한 점, 개선점 등 객관적이고 구체적인 정보 위주로 작성해 주세요.
결과는 아래 형식으로 출력해 주세요.
[기준별 점수]
정확도: 45 / 50
가독성: 15 / 20
효율성: 10 / 15
분석력: 10 / 15
[기준별 참고사항]
정확도: WHERE 조건에서 'Attrited Customer' 필터가 누락됨.
가독성: 쿼리 구조는 명확함.
효율성: CTE 활용이 부족함.
[최종 점수] 80
[피드백] 전반적으로 쿼리 구조는 명확하나, 일부 필터 조건과 효율성에서 아쉬움
문제: 프롬프트에 대한 불확실성
결국 LLM에 응답한 결과는 매번 결과가 달라지는 문제가 있습니다. 특히 이따금 결과값이 json 형태로 뱉어내어서 기대한 형태의 출력이 안되는 경우도 많았는데, 추후에 Langhain 스택을 추가하여 StructuredOuputParser 사용하는게 좀 더 나을 것 같습니다.
기능2) 채점 기능 - SQL
구현 내용
SQL은 MySQL로 문제를 냈다면 해당 클라우드가 있어야합니다. 이를 위해 Cloud SQL에 인스턴스를 하나 파서 DDL을 이용하여 데이터를 삽입하였습니다. 문제는 공통된 DDL을 코드로 제시했음에도 불구하고 각자의 환경에서 알아서 데이터를 불러오다보니 미묘하게 결과 데이터가 다른 문제가 있었습니다. 예를 들어 정수/정수 나누기에 대한 결과값 계산 과정에서 decimal 사용 유무에 따라 결과 값이 달라서 결국 소수점 2째짜리 이내는 정답으로 처리하도록 임시 방편을 택했습니다.
문제: 비용 이슈
반면 cloud 스펙에 대한 이슈도 있었는데, 원본 데이터가 그렇게 크지 않지만 초기 스펙은 램 2기가에 2CPU를 기본값을 배정했는데 매일 5천원씩 너무 많이 과금이 되는 문제가 있었습니다. 샌드박스 옵션인데도 불구하고 과제에 적합한 사이즈는 아니 였으며, DB의 완정성을 보장하기 위한 백업이나 관리의 비용이 많이 나가는 것을 확인했습니다. 이를 확인하고 다운 그레이드 절차를 진행했습니다. 결과적으로 일 5000원 유지비 -> 850원 정도의 유지비로 절감할 수 있었습니다.
데이터 백업 기능 해제(운영 DB가 아니므로)
CPU 2코어 수 -> 1 햐향 조정
메모리 2GB -> 600 MB 하향 조정
기능2) 채점 기능 - Python / Pandas
구현 내용
파이썬의 경우내장 함수 exec를 통해 채점을 구현했습니다. 문제를 dictionary 형식으로 저장하여 추후 불러와 사용했습니다. python의 결과는 모범정답의 key값에 따라 다양한 체크포인트를 거쳐 최종 정답으로 처리됩니다. 예컨데 문제1번이 단순하게 평균을 내는 함수라면 model_answer의 코드를 exec 내장함수에 넣고 test_cases의 input값을 전달인자로 실행하여 expected와 같은지 확인하면 됩니다.
QUESTIONS = {
"PYTHON_1": {
"title": "숫자 리스트의 평균 계산하기",
"content":
"""
- **배경**: 한 소매점에서 재고를 계산해야 합니다. 주어진 재고의 평균을 계산해보세요.
- **문제 의도**
- 리스트의 자료형을 이해
- 내장 함수의 활용
- **요구 사항**
- 함수명: `calculate_stock`
- 해당 함수는 리스트의 전달 인자를 받음
""",
"model_answer":
"""
def calculate_stock(numbers):
return sum(numbers) / len(numbers)
""",
"function_name": "calculate_stock",
"test_cases": [{"input": [10, 20, 30, 40, 50], "expected": 30.0}],
"evaluation_criteria": [
{"id": "P1", "description": "리스트의 합과 길이를 이용해 평균을 계산한다."},
{"id": "P2", "description": "내장 함수(sum, len) 사용"}
]
}
Pandas의 경우 자료형과 결과값 두개를 동시에 비교해야 하기때문에 "test_cases"에 자료형을 저장하는 "expected_type"과 "expected"를 동시에 넣어 채점하게 됩니다. passed 변수를 True로 초기화 해놓고 각 기준을 통과하지 못하면 False으로 Flag를 세워 최종적으로 True로 살아남는 코드를 정답으로 처리하였습니다.
"PYTHON_7": {
"title": "결측치 확인",
"content":
"""
- **배경:** 데이터를 불러 왔을 때 각 컬럼에 결측치 유무를 확인하는 것은 중요합니다. 컬럼의 결측치를 확인해보세요.
- **문제 의도**
- DataFrame의 함수를 활용
- **요구 사항**
- 함수명: `get_missing`
- 컬럼별 결측치 수를 예시 결과와 같이 출력
""",
"model_answer":
"""
def get_missing(df):
return df.isnull().sum()
""",
"function_name": "get_missing",
"test_cases": [ {"input": "df_sample",
"expected_type": 'Series',
"expected": {
"Route": 1,
"Total_Stops": 1,
"Airline": 0, "Date_of_Journey": 0, "Source": 0,
"Destination": 0, "Dep_Time": 0, "Arrival_Time": 0,
"Duration": 0, "Additional_Info": 0, "Price": 0
}}
],
"evaluation_criteria": [{"id": "P1", "description": "DataFrame의 isnull().sum()을 활용한다."}]
}
또한, 데이터를 불러와야하는 문제가 있었는데, 매번 문제마다 데이터를 불러오면 in-ouput관점에서 비효율적일 것 같아서 찾아보니 stremlit에서 캐시 기능을 구현할 수 있는 데코레이터를 발견했습니다. 이를 사용해서 효율적인 저장을 구현했습니다.
@st.cache_data
def load_sample_dataframe():
"""
이 함수는 처음 호출될 때 단 한 번만 실행되고 캐싱됩니다.
이후 호출에서는 캐시된 데이터를 반환합니다.
"""
# print('데이터로드 시작')
df = pd.read_csv('./data/7th/flight_data.csv', sep=';')
# print(df.head(3))
# print('데이터로드 종료')
return df
이슈: 데이터 로딩 문제
문제는 Pandas에 있었는데, 데이터 분석에 관련하기 때문에 외부에서 데이터를 불러와서 채점하게 됩니다. uci 레포나 github 등일수도있고 mysql 서버일 수 도 있습니다. 하지만 cloud service은 외부 데이터를 가져옴에 있어서 인증을 요구하다보니 이걸로 3일 정도 삽질을 했습니다. 사실 별거아닌 문제이지만, cloud run 서비스 특성상 에러를 찾기가 매우 불편합니다. 로그를 뒤져보면 되긴하지만 내가 실제로 서버를 띄우는 것 처럼 bash형태로 로그를 찾는게 아니다 보니 데이터 로딩에서 문제가 일어난다는 사실 자체를 찾는데도 오래 걸렸습니다. 사실 데이터가 7MB 이였어서 큰 문제는 없었지만, 대량의 데이터를 불러온다면 Cloud SQL 에 적재해서 가져오는 방향이 나을 것 같습니다.
4. 개발 과정의 교훈과 팁
각 디렉토리를 정확하게 명시
― core: 핵심 모듈
┕ service: 외부 호출되는 서비스( Ex Cloud SQL 등)
┕ streamlit_app: 프론트 엔드 streamlit 을 위한 디렉토리
┕ answer: 정답 저장
┕ data : 데이터 저장
하나의 디렉토리는 하나의 기능 혹은 구현하려고자하는 결과물이 명확하게 정의되면 정리하기가 좋습니다. 혼자 일하던 여럿이 일하던 명확한 디렉토리 설계와 README.md 작성은 업무 시작 시 로딩 속도를 줄여줍니다.
생성된 모듈의 import 하는 방법
# module import 문제 없음. 같은 경로
/ main.py
/ module.py
# module import 문제 없음. 같은 경로
┖ core
┖ ─ main.py
┖ dir
┖ ─ __init__.py
┖ ─ module.py
위처럼 모듈을 기능에 따라 나누다보면 결국 타 디렉토리에 모듈을 가져올 상황이 생깁니다. 같은 경로면 import module.py 를 스크립트 상단에 올리면되지만 디렉토리가 서로 다른 경우 문제가 생깁니다. 이럴때 `__init__.py` 라는 초기화 모듈을 해당 디렉토리에 넣어주면 경로를 쉽게 인식할 수 있게 해줍니다.
Docker를 적극적으로 사용하자
streamlit 을 이용하면 localhost:8501 포트에 자동으로 실행되지만 여기서 재현성이 된다고 Cloud run에서 잘 작동하는 것 은 아닙니다. 기본적으로 로컬은 윈도우 환경이고 cloud 서비스는 linux 환경이기 때문입니다. 그래서 실제로 컨테이너를 띄워서 테스트해보는게 cloud run에 배포했을 때도 오류를 줄일 수 있는 좋은 테스트 환경입니다. Docker 짱장맨!
# base 디렉토리로 이동
cd base
# Docker 이미지 빌드
# 맥이라면 arm 64 아키텍쳐로 빌드하고 테스트하며, 배포 시에는 arm64로 변경필요
docker build --platform linux/amd64 -t {서비스명} .
# 빌드된 이미지로 Docker 컨테이너 실행하기
docker run -d -p 8501:8080 -m 1g --name {개발 이미지명} {서비스명}
[중요] build후 로컬 테스트에서 윈도우에서는 amd64, 맥에서는 arm64 아키텍쳐를 명시해야하나, cloud run 배포시는 amd64로 통일 필수(이것 때문에 단순배포 2시간동안 삽질함)
.env: 환경 변수에 대한 정보를 담습니다. 보통 api나 db의 접속 정보 등을 가지고 있으나 credential이기 때문에 `.env .example` 파일을 github에 올리고 환경변수 명을 제외한 정보를 제거하여 형태만 보존합니다.
# .env.example
GEMINI_API_KEY=
SLACK_BOT_TOKEN=
`.dockerignore`: 도커나이징 할때 굳이 필요 없는 파일을 저장합니다. `.gitignore`와 비슷한데 보통 python 가상환경에 대한 디렉토리나 cache 기타 파일들을 작성합니다. 이걸 명시하지 않으면 가상환경 디렉토리 내용이 통째로 docker화 되기 때문에 작업 효율이 늦어집니다.
# 파이썬 가상환경 폴더
.venv
# 파이썬 캐시 파일
__pycache__/
*.pyc
# Git 폴더
.git
# 기타 운영체제 파일
.DS_Store
배포 코드 .sh 화: 일련의 deploy를 위한 코드를 매번 치지말고 .sh 파일을 만들어서 실행시킵니다.
#!/bin/bash
# 스크립트 실행 중 오류 발생 시 즉시 중단
set -e
# 2. Docker 이미지 빌드 (linux/amd64 플랫폼)
echo "Building Docker image..."
docker build --platform linux/amd64 -t {서비스명} .
# 3. Artifact Registry에 맞게 이미지 태그 지정
echo "Tagging image for Artifact Registry..."
docker tag {서비스명} {이미지명}.pkg.dev/{프로젝트}/cloud-run-source-deploy/{서비스명}
# 4. Artifact Registry로 이미지 푸시
echo "Pushing image to Artifact Registry..."
docker push {이미지명}.pkg.dev/{프로젝트}/cloud-run-source-deploy/{서비스명}
# 5. Cloud Run에 배포 (us-central1)
echo "Deploying to Cloud Run..."
gcloud run deploy grading-app \
--image={이미지명}.pkg.dev/{프로젝트명}/cloud-run-source-deploy/{서비스명} \
--region=us-central1 \
--platform=managed \
--vpc-connector={vpc 커넥터} \
--vpc-egress=all \
--allow-unauthenticated \
--memory=2Gi \
--min-instances=1 \
--cpu=2
echo "Deployment complete! 🚀"
requirements
requirement는 가상환경을 쓴다면 자주 사용하는데 이게 나중에 docker로 배포할때 설치할 라이브러리가 되기 때문에 필수적입니다.
#가상환경 만들기
py -3.12 -m venv {가상환경명}
'''
라이브러리 설치 ...
'''
#패키지 저장
pip freeze > -r requirements.txt
#설치
pip install -r requirments.txt
mono repo전략
잠시 파이썬 채점자동화시 clodu run function을 고민했던 적이 있었습니다. 코드저장소 측면에서보면 하나의 리포지토리에 두 개이상의 배포 포인트가 있어야하는건데 이럴 땐 그냥 간단히 디렉토리를 나누면 됩니다.
5. 개발 후기
5.1. 그래서 cloud run 다음에도 쓸까?
상황에 따라 다를 것 같은데, 그래도 서버를 파는게 나을 것 같습니다. on-demand서비스라는건 비용 측면에서 좋지마 로그를 찾기가 너무 불편합니다. 데이터 로딩 과정에서 계속 에러가 났는데 확인하기 불가능한 경우가 많았습니다. 더욱이 로컬 Docker에서는 기능을 잘 하는 것이 배포하는 환경에서는 에러 재현이 안되니 정말 답답했습니다. 사실상 단일 스크립트로 구현해야하는 서비스의 경우에는 cloud run function을 사용하는게 나을 것 같고, 조금 복잡한 서비스에는 직접 GCE를 이용해서 구현하는게 디버깅 측면에서는 더 나을 것 같습니다.
여기서 에러를 찾아보시오..
5.2. 개선할 점
구조설계의 효율화
뒤돌아보니 개선할 몇가지가 보이긴 합니다. 일단 가장 큰 것은 함수의 파편화입니다. 일례로 gemini가 정성 평가하는 방식만 봐도 main -> local_grader -> generate_content -> prompt_builder 의 스택 구조가 필요 이상으로 만들어져 있다는 느낌이 들었습니다. 이러면 디버깅 관점에서도, 확장 측면에서도 파악이 어려워서, 다음에는 하나의 스크립트로 통합하고 prompt_builder 와 같은 지속적으로 변경하고 수정이 필요한 부분만 따로 분리하는 것이 좋을 것 같습니다. 또한, 함수형 스크립트보다는 문제의 기능을 통합하여 class 설계를 하는 것도 좋겠습니다.
비용 효율 관점
처음에는 비용을 크게 생각하지 않았는데, 매일 몇천원씩 과금되는 Cloud - SQL이 점차 비용이 많이 증대되는게 느껴졌습니다. 좀 더 저렴한 사설 cloud를 쓸 수 도 있겠지만, 관리포인트가 많아지고 난이도가 높아질 것 같아 최대한 GCP안에서 해결하려고 했습니다. 하지만 그 과정에서도 자원과 비용을 효율적으로 사용할 설계를 하는 것이 좋겠습니다. 항상 채점 서비스가 올라가야하는 것은 아니라 상시 올려져있는 cloud-sql와 같은 서비스도 on-off 자동화가 필요해보입니다.
채점 서비스 기능 추가
현재 채점 서비스는 따로 로깅이나 데이터 수집을 하고 있진 않습니다. Gemini를 단순히 사용하는 것에 넘어서 개선하는 결과를 만들고 싶은데 그러러면 로깅 시스템이 있어야해서 설계해볼 것 같습니다. 또한, 현재는 정량적인 결과를 채점하고 있는데, 추후 통계 그래프나 시각화 이미지 등을 채점하는 기능을 추가 개발할 여지가 있습니다. 그리고 현재는 Prompt Engineering이 직접 api를 call 하고 있는데 langchain을 도입하여 좀 더 구조적인 설계를 해볼 수 있습니다.
5.3. 결과
결과적으로 총 2번의 과제에 지원 서비스를 무사히 마칠 수 있었고, 하나의 서비스를 개발해보는 좋은 경험이 되었습니다. 정량적으로는 기존에 1명에 채점이 20분 정도 걸리는 시간에서 10분정도로 빠르게 채점할 수 있었고, 개별적으로 튜터들이 시스템을 구축하여 채점하는 사전 준비 시간도 없앨 수 있었습니다. 사실 본 서비스는 누가 시킨것도 아니고 스스로 편하게 일해보자고 시작했지만 그래도 잘 해보자 나에게 남을 것이라는 마음으로 시작했는데, 결과가 나름 만족스럽게 나와서 좋았습니다. 다음에도 이렇게 스스로 추진하고 결과를 만드는 프로덕트를 하나씩 만들고 남겨보도록 하겠습니다.
from dotenv import load_dotenv
import getpass
import os
load_dotenv(verbose = True)
# os.getenv("GOOGLE_API_KEY")
if "GOOGLE_API_KEY" not in os.environ:
os.environ["GOOGLE_API_KEY"] = getpass.getpass("Enter your Google AI API key: ")
NVIDA에서 모델 불러올 수 있는데 황당한건 모델 컨테이너 사이트에서 한국인증이 아직 안됩니다.. 어째서..?
2. 본문 불러오기
from langchain_google_genai import ChatGoogleGenerativeAI
llm = ChatGoogleGenerativeAI(model="gemini-2.0-flash")
result = llm.invoke('What is OMSCS?')
print(result.content)
"""
OMSCS stands for **Online Master of Science in Computer Science** offered by the **Georgia Institute of Technology (Georgia Tech)**.
Here's a breakdown of what makes it significant:
* **Reputable Institution:** It's a fully online master's degree from a top-ranked computer science program. Georgia Tech is highly respected in the field.
* **Affordable:** Compared to traditional on-campus master's programs, OMSCS is significantly more affordable. This makes it accessible to a wider range of students.
* **Flexible:** The online format allows students to study at their own pace and on their own schedule, making it ideal for working professionals.
* **Rigorous Curriculum:** The program covers a wide range of computer science topics and maintains a high academic standard. It is not a "watered down" version of the on-campus program.
* **Diverse Specializations:** Students can choose to specialize in areas such as:
"""
3. 스트리밍과 배치
invoke와 대응되게 대답을 받는 방법이며, streaming은 동기적으로 batch는 비동기 형식으로 대답을 반환
# llm이 뱉어내는 결과를 스트리밍하게 밷어낼 수 있다.
# invoke는 동기식, stream은 비동기식으로 결과를 받아온다.
prompt = "What is OMSCS?"
for chunk in llm.stream(prompt):
print(chunk.content, end='')
#batch 식
prompts = [
'What is OMSCS?',
'What is the capital of France?',
'What is the largest mammal?'
]
results = llm.batch(prompts)
for result in results:
print(result.content)
4. 템플릿을 이용한 재사용
def translate_to_french(text):
prompt = f"Translate the following English text to French: '{text}'"
return llm.invoke(prompt).content
english_statsments = [
"Hello, how are you?",
"What is your name?",
"I love programming.",
"The weather is nice today."
]
prompts = [translate_to_french(statement) for statement in english_statsments]
prompts
"""
['Here are a few options for translating "Hello, how are you?" into French, with slightly different nuances:\n\n* **Bonjour, comment allez-vous ?** (Formal, polite. Use with people you don\'t know well, or in professional settings.)\n\n* **Salut, comment vas-tu ?** (Informal, friendly. Use with friends, family, and people you know well.)\n\n* **Bonjour, ça va ?** (Informal, but very common. A general greeting, like "Hi, how\'s it going?")\n\n* **Salut, ça va ?** (Even more informal, like "Hey, what\'s up?")\n\nThe best choice depends on the context and your relationship with the person you\'re speaking to.',
'The most common and polite translation of "What is your name?" in French is:\n\n**Comment vous appelez-vous ?**\n\nHere are a few other options, with slightly different connotations:\n\n* **Quel est votre nom ?** (More formal, literally "What is your name?")\n* **Tu t\'appelles comment ?** (Informal, used with people you know or are on familiar terms with)\n* **Comment tu t\'appelles ?** (Also informal, but slightly more direct than "Tu t\'appelles comment ?")\n\nThe best choice depends on the context and your relationship with the person you\'re asking. However, "Comment vous appelez-vous ?" is generally the safest and most appropriate option.',
'The most common and direct translation of "I love programming" in French is:\n\n**J\'adore la programmation.**\n\nHere are a few other options, with slightly different nuances:\n\n* **J\'aime la programmation.** (This is also very common and means "I like programming," but can also imply love in some contexts)\n\n* **J\'aime beaucoup la programmation.** (This emphasizes the liking, "I like programming a lot")\n\n* **Je suis passionné(e) par la programmation.** (This means "I am passionate about programming." Use "passionné" if you are male, and "passionnée" if you are female.)\n\n* **La programmation, c\'est ma passion.** (This means "Programming is my passion.")\n\nThe best choice depends on the specific context and the degree of enthusiasm you want to convey. However, **J\'adore la programmation** is generally the safest and most natural translation for "I love programming."',
'There are a few ways to translate "The weather is nice today" into French, depending on the nuance you want to convey:\n\n* **Il fait beau aujourd\'hui.** (This is the most common and straightforward translation. It means "The weather is good today.")\n\n* **Le temps est agréable aujourd\'hui.** (This is a slightly more formal way of saying it, and means "The weather is pleasant today.")\n\n* **On a du beau temps aujourd\'hui.** (This is a more colloquial way of saying it, and translates to "We have nice weather today.")\n\nSo, the best option depends on the context, but **Il fait beau aujourd\'hui** is generally the safest and most widely understood.']
"""
translations = llm.batch(prompts)
for translation in translations:
print('---')
print(translation.content)
"""
---
This is a great and accurate breakdown of the different ways to say "Hello, how are you?" in French! You've covered the key distinctions of formality and provided helpful context for each option.
Here are a few minor additions that could further enhance this explanation:
"""
#template 으로 프롬프트 엔지니어링 하기
from langchain_core.prompts import ChatPromptTemplate
#사전에 템플릿을 만들어 runnable을 만드는 방식
english_to_spanish_template = ChatPromptTemplate.from_template("""Translate the following English text to Spanish Provide only the translated text: {english_statement}""")
prompt = english_to_spanish_template.invoke('Hello, how are you?')
print(llm.invoke(prompt).content)
6. 예시
다음 문장들을 가지고 3가지(감성분석, 메인 토픽 추출, 다음에 올 질문 만들기)
statements = [
"I had a fantastic time hiking up the mountain yesterday.",
"The new restaurant downtwon serves delicious vegeterian dishes.",
"I am feeling quite stressed about the upcoming project deadline.",
"Watching the sunsset at the beach was a calming exmperience.",
"I recently started reading a fascinating book about space exploration.",
]
sentiment_template = ChatPromptTemplate.from_template("In a single word, either 'positive' or 'negative' provide the overall sentiment of " \
"following piece of text: {text}")
main_topic_template = ChatPromptTemplate.from_template("Identify main topic with one sentence about following piece of text: {text}")
followup_template = ChatPromptTemplate.from_template("what is followup just one question about following piece of text: {text}")
sentiment_prompts = [sentiment_template.invoke({"text":statement}) for statement in statements ]
main_prompts = [main_topic_template.invoke({"text":statement}) for statement in statements ]
followup_prompts = [followup_template.invoke({"text":statement}) for statement in statements ]
sentiments = llm.batch(sentiment_prompts)
main_topics = llm.batch(main_prompts)
followups = llm.batch(followup_prompts)
for statement, sentiment, main_topic, followup in zip(statements, sentiments, main_topics, followups):
print(
f"Statement : {statement}\n"
f"Overall sentiment : {sentiment.content}\n"
f"Main topic : {main_topic.content}\n"
f"Followup question : {followup.content}"
)