안녕하세요. 강의에서 나오는 BitsAndBytesConfig 를 통한 양자화 예시에서 CUDA 가 필수로 요구되어 macbook에서 진행할 수 있는 방안을 찾다가 mlx_lm 을 통해서 MPS(mac의 gpu)를 활용하여 양자화가 가능하단 사실을 알게되어 공유드리고자 합니다. 아마 맥북 사용자도 꽤 많을 것으로 예상됩니다. langchain에서 MLXPipeline 을 제공하지만 이를 사용했을 때 현재 invoke시 호출되는 _call 메서드 내부에서 generate_step 이 mlx_lm에서 제공은 하지만 MLXPipeline 에서 제대로 불러올 수 없어 RunnableLambda 를 통해서 에러를 우회해봤습니다.(Q? 강사님은 혹시 해결 방법을 알고 계실까요? 공홈 코드가 다 안 되네요 ㅜㅜ 오류 코드는 맨 아래 첨부하겠습니다. ) 먼저 양자화 진행하는 방식입니다. cli를 통한 command 또는 python-api 를 활용하는 방법 두 가지가 있습니다. 자세한건 링크 첨부하겠습니다. 양자화 개념에 대해서 자세히 나오니 참고하면 좋으실 듯 합니다. 링크: [quantization with xlm_lm]( https://developer.apple.com/kr/videos/play/wwdc2025/298/?time=187 ) mlx_lm을 통한 양자화 먼저 양자화한 model을 local path에 저장합니다. 그 전에 apple의 gpu를 확인할 수 있는 코드 부분은 처음 부분에서 확인하실 수 있습니다. 사실 python코드를 싸는 것보다 command로 양자화 진행하는게 편한 것 같습니다. 전 projection layer와 embedding layer는 다른 layer보다 높은 bit로 해주는게 좋다고 하여 python으로 진행했습니다. dequantize on-the-fly(게산 추론) 시 더 좋다고 합니다. # mac에서 mps를 사용한 예제 import torch from mlx_lm.convert import convert print("MPS available on this device =>", torch.backends.mps.is_available()) # projection layer & embedding layer는 6bit, 양자화 가능한 layer는 4bit, 양자화 불가능은 False return def mixed_quantization(layer_path, layer): if "lm_head" in layer_path or 'embed_tokens' in layer_path: return {"bits": 6, "group_size": 64} elif hasattr(layer, "to_quantized"): return {"bits": 4, "group_size": 64} else: return False # quantization 진행 convert( hf_path="microsoft/Phi-3-mini-4k-instruct", mlx_path="./models/microsoft-Phi-3-mini-4k-instruct-mixed-4-6-bit", dtype="float16", quantize=True, q_bits=4, q_group_size=64, quant_predicate=mixed_quantization ) $) mlx_lm.convert --hf-path "mistralai/Mistral-7B-Instruct-v0.3" \ --mlx-path "./mistral-7b-v0.3-4bit" \ --dtype float16 \ --quantize --q-bits 4 --q-group-size 64 --upload-repo "my-name/mistral-7b-v0.3-4bit" Langchain과 연계하기 from functools import wraps from langchain_core.runnables import RunnableLambda from langchain_core.output_parsers import StrOutputParser from mlx_lm import generate, load quantized_model_path = "./models/microsoft-Phi-3-mini-4k-instruct-mixed-4-6-bit" model, tokenizer = load(quantized_model_path) def runnable_wrapper(func): """RunnableLambda wrapper function""" @wraps(func) def wrapper(*args, **kwargs): return RunnableLambda(func) return wrapper @runnable_wrapper def create_chat_prompt(question): messages = [ { "role": "system", "content": """ You are an expert in information retrieval and summarization. Your job is to read the provided text and produce a precise, faithful, and concise summary. Prioritize the author’s main claim, key evidence, and conclusions. Use plain English and avoid filler. Do not invent facts that aren’t present in the input. """ }, { "role": "user", "content": f""" Question: {question} """ } ] prompt_without_tokenized = tokenizer.apply_chat_template(messages, add_generation_prompt=True, tokenize=False) prompt_with_tokenized = tokenizer.apply_chat_template(messages, add_generation_prompt=True) print("생성된 prompt👇\n", prompt_without_tokenized) return prompt_with_tokenized @runnable_wrapper def run_llm_with_mlx(prompt): return generate(model=model, tokenizer=tokenizer, prompt=prompt) @runnable_wrapper def output_parser(answer): return answer.replace("<|end|>", "") chain = create_chat_prompt() | run_llm_with_mlx() | output_parser() 오류 코드 llm.invoke 에서 에러가 발생하며 TypeError: generate_step() got an unexpected keyword argument 'formatter' 라는 에러가 발생합니다. from langchain_community.llms.mlx_pipeline import MLXPipeline from mlx_lm import load model_path = "./models/microsoft-Phi-3-mini-4k-instruct-mixed-4-6-bit" model, tokenizer = load(model_path) llm = MLXPipeline( model=model, tokenizer=tokenizer ) llm.invoke("what's my name?")
학습 관련 질문을 최대한 상세히 남겨주세요! 고민 과정도 같이 나열해주셔도 좋습니다. 먼저 유사한 질문이 있었는지 검색해보세요. 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요. 안녕하세요 선생님! 지난번에 남겨주신 답변내용을 보면서 전략도입의 배경부터 처리과정까지 복기해보았습니다. 그러다가 의문이 생긴 점이 있습니다. 1) 의문점 Request Collapsing, 캐싱중복적재를 제외하고 캐싱하여 데이터를 추출하는 과정만을 보았을때, 원본과 캐싱 데이터 모두 Redis에서 추출해오는 것인데, 원본추출과 비교하였을때 과정적으로 어떠한 차이점이 추가적으로 있있기에 별도의 과정으로 간주하는 것일까? 즉, "캐싱하여 가지고 오는 과정"과 "원본 데이터 추출"을 따로 보고 계셨기에, 어떠한 차이점이 있는지 의문점이 들었습니다. 2) 의문점이 생긴 이유 단순하게 간략히 말씀드리자면, 조회수 원본데이터는 Redis에서 가져오는 조회수이고, 캐싱해서 가져오는 것 역시 Redis에서 가져오는 조회수로 보여집니다. 원본데이터가 MySQL과 같은 디스크 조회 비용이 큰 저장소에 들어있는 것이 아니라, 동일한 In Memory database인 Redis에서 가져오는 것이기에 성능/비용적으로 캐싱 데이터를 가지고 오는 것에 큰 이점을 느끼지 못하였습니다. 세부적으로 살펴보았을때, ViewClient에서 원본 데이터를 가지고 오는 경우 아래 로직을 통해 Redis에서 추출합니다. articleViewCountRepository.read(articleId); 이떄 key는 view::article::#articleId::view_count 입니다. ViewClient에서 Aspect를 처리하여 캐싱 데이터를 가지고 오는 경우 Redis에서 추출합니다. 이때 key는 articleViewCount::#articleId입니다. 이 과정에 대해 캐싱을 하는 목적을 생각해보았을때(=원본데이터 추출에 시간이 오래 걸릴 경우 성능이 빠른 다른 데이터저장소를 운용하여 이곳에서 데이터를 추출해오기 위함), 동일하게 Redis에서 추출하는 데이터임에에도 key를 별도로 운용하고, 데이터를 추출하는 과정도 다르게 가져가는(간주하는) 이유가 무엇인지 궁금하여 문의코자 합니다(물론 전체 로직을 살펴보았을때는 기존 대비 중복적재/Request Collapsing 과정을 추가하였기에 당연히 성능적인 이점이 존재하겠지만, 데이터를 가져오는 과정 그 자체만을 보았을때는 의문점이 들었습니다). 이게 데이터를 추출하는 과정이 다르기에, 동일한 key로 운용하면 실무적으로 로직이나 key관리방안이 복잡해져서 관리의 효율화를 위해 나누는 것일까요(즉, 기존과 달리 분산락도 사용하고 중복적재를 방지하기 위해 이용하므로 목적 자체가 다르기에 key 포맷 및 캐싱을 별도로 운용하는 것으로 이해하는 것이 적절한지)? 제가 캐싱의 목적 부터 잘못 이해하고 있는 것일 수 있고, 실무적으로 비용/성능적 유리하다는 의미가 무엇인지 잘못 이해하고 있는 것일 수 있다고 생각하고 있습니다. 그렇기에 챗지피티에게 물어보기보다는 실무적으로 경험이 풍부하시고 그만큼 검증된 선생님의 판단이 더 정확하고 궁금하여 질문드리게 되었습니다. 바쁘신데도 항상 성심성의껏 답변해주시는 선생님께 감사의 말씀 드립니다. 이효균 드림.
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요. 1. 강의 내용과 관련된 질문을 남겨주세요. 2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요. (자주 하는 질문 링크: https://bit.ly/3fX6ygx) 3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요. (질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG) 질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요. ========================================= [질문 템플릿] 1. 강의 내용과 관련된 질문인가요? (예/아니오) 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) [질문 내용] 여기에 질문 내용을 남겨주세요. 프로젝트 지웠다가 처음부터 다시 해보기도 하고 질문글 따라 보면서 해봐도 에러가 떠서 어떻게 해야할지 모르겠어요.. .
정보처리기사 실기를 선생님과 함께 하고, 시험 합격에 이어서 이제 본격적으로 취업을 위한 저만의 프로젝트를 만들어 보겠다는 생각이 들었습니다. 마침 Cusor AI의 첫 개강 때 할인 이벤트가 있어서 저만의 첫 프로젝트를 시작하기 전에 이번 기회에 다시 한번 더 선생님과 함께 나아가고자 현재 선생님의 새로운 강의를 수강하고 있습니다. 그리고 어제 새벽에 처음 시작해서 정신을 차려 보니 지금 벌써 4강을 진입하는 상황이 되어버렸습니다.. 결론은 또 엄청난 강의를 만들어 주신 것 같습니다....! 이제 정말 질문인데요. 프론트 엔드의 영역이라 생각하고 지금 재미있게 공부 중에 있습니다만 백엔드나 전체적으로 다뤄서 풀스택 관련 강의 제작도 목표가 있으실지 해서 질문 남겨드립니다. 현재 독학으로 Spring을 공부 중에 있습니다만, 선생님의 자바 백엔드 수업도 있다면 재미있게 제가 원하는 개발을 이어나갈 수 있을 것 같아 조심스럽게 이 자리에 질문을 남겨드립니다. 아무쪼록 이번에도 놀랍고 대단한 몰입력을 유도하는 강의를 만들어 주셔서 정말 감사합니다. 정보처리기사 합격에 이어서 이번에는 선생님 강의를 듣고 취업 성공까지 힘내 보겠습니다!ㅎ (ps 시험 때문에 듣던 선생님의 ppt 자료가 이제는 시험이 아니라 재밌는 창작 같은 공부에 쓰이니까 모든 게 감회가 새롭네요ㅋㅋㅋ)
21:09 지금 까지 이해한 포인터의 방법은 주소 사용할때는 & 또는 생략, 값을 사용할때는 *로 이해하고 있습니다. 해당 printf("%s\n", arr[0][0]); 라는 구문에 질문이 있습니다. 포인터 변수로 해당 문자열의 주소를 각 맵핑하였고 배열에 맞게 출력하는걸로 해석이 되는데 이때 왜 값을 안 가져오고 주소를 가져오는지 궁금합니다. 또한 %s로 출력하고 해서 주소대신에 값으로 가져오는지 궁금합니다.
안녕하세요 강사님, 공지를 늦게 확인하고 쿠폰을 확인하지 못해 문의 남깁니다. 이전 문의 글들을 보니 가입한 이메일로 지난주 및 이번주에 쿠폰이 지급될 것이라 안내 해 주셨더라구요. 하지만 저 같은 경우 지난주에도 메일이 오지 않았고 현재도 수신된 메일이 없기에 확인할 수 있는 방법이 있을지 문의 드립니다. 감사합니다!
유형 9번에서 처음에 Child obj = new Child(); 의 뜻이 Child 타입의 새로운 생성자를 생성하는 것이고, 이 생성자의 자식 class로 갔는데, 생성자가 없고, 부모에도 생성자가 없어서 출력되는 값은 없는 것이고, 그 뒤의 obj.display()코드를 실행해 display의 메서드에서 부모로부터 상속받아 Parent display, Child display가 답이 되는것이 맞을까요?
안녕하세요. 궁금한게 있어서 질문 드립니다. domain 패키지에 entity를 위치 한다고 했을때 주석이라고 말씀하신 어노테이션 JPA @Entity를 만들어서 Member로 정의 그리고 mongo DB도 필요해서 @Document를 정의해서 domain 패키지에 위치 시켜 Member로 정의 이처럼 같은 도메인이 서로 다른 DB로 사용 될때 어떻게 도메인으로 정의해야할지 궁금합니다. 그리고 이름이 중복되는 현상도 발생하기도 합니다. 그렇다고 OracleMember, MongoMember로 지을수도 없는것같습니다. 제 생각은 Member라는 도메인이 Domain 패지키에 별도로 나와서 adapter 안에 JPA Persistent의@Entity로 정의된 클래스와 Mongo Persistent의 @Document로 정의된 클래스가 위치 되어야한다고 생각하는데 어떤 의견이신지 궁금합니다.