마키나락스
경력근거: LLM 실적용 성공 사례를 지속적으로 늘려나가고 전체 수행 과정 효율을 개선할 수 있는 체계를 함께 고민하여 반영합니다.
마키나락스 FDE 본부는 산업 현장에 나가 AI 를 실제로 얹는 조직이고, 이 포지션은 최신 LLM 을 On-Device NPU 환경에 탑재하기 위한 모델 변환·검증과 양자화를 PyTorch·HuggingFace·ONNX 로 정량 분석합니다. 고객사 단말의 메모리·지연 예산을 제약으로 놓고 레이어별 비트 배분을 계산해 답하는 이 계산기는 정량적으로 분석하고 개선한다는 업무를 숫자가 나오는 산출물로 옮긴 것입니다. 다만 이 포트폴리오가 보여주는 것은 예산 제약 아래 배분을 계산·탐색하는 최적화 엔지니어링이며, 양자화 커널 구현·NPU 하드웨어 최적화 같은 도메인 전문성은 주장하지 않습니다. 사양과 프로파일을 예산 판정과 부족분 수치로 잇는 사슬은 단순 지표 비교 맥락으로는 그대로 만들 수 없습니다.
최신 LLM 을 온디바이스 NPU 에 탑재하기 위한 모델 개발·변환·검증과 양자화 기술을 연구·적용합니다. 예산 안에 들어가는 비트 배분 계산기가 직접 정렬됩니다.
PyTorch·HuggingFace·TFLite·ONNX·AIMET 등으로 모델 성능을 정량 분석·개선합니다. 메모리·지연·품질 손실을 수치로 답하는 계산이 환경과 정렬됩니다.
현장 데이터로 AI 문제를 정의하고 End-to-end 머신러닝 라이프사이클을 경험합니다. 고객사 단말 사양을 받아 예산을 계산하는 흐름이 비즈니스와 정렬됩니다.
고객사 하드웨어팀이 준 단말 NPU 사양(가용 메모리·대역폭·지원 정밀도·목표 지연)과 모델에서 뽑은 레이어 프로파일(파라미터 수·활성값 이상치 비율·비트 강등 민감도)을 받아, 레이어마다 int4·int8·fp16 중 무엇을 줄지 계산합니다. 메모리 점유·추정 지연·추정 품질 손실을 공식으로 계산하고, 예산을 만족하는 조합 중 손실이 가장 작은 배분안을 결정론 탐색으로 고른 뒤 남는 여유와 불가 사유를 터미널 표로 답합니다. 배분 선택은 계산이 하고 LLM 은 사양서의 자유 서식 메모를 숫자로 옮기고 결과를 한 문단으로 설명하는 보조만 맡습니다. 탑재 확정이 아니라 예산 검토용 산출입니다.
마키나락스 FDE 팀이 들어간 계약 고객사(온디바이스 AI 를 얹는 단말 제조사)의 임베디드 AI 담당자입니다. 하드웨어팀이 준 사양서와 모델 프로파일을 스프레드시트에 옮겨 적어 이 모델이 우리 칩에 들어가는지를 손으로 셈하던 일을, 한 번의 명령으로 배분안과 남는 메모리를 받고 싶은 사람입니다.
단말 사양과 레이어 프로파일을 한 스키마로 묶으면, (a) 메모리 점유·지연·품질 손실을 공식으로 계산하고, (b) 예산을 만족하는 비트 배분 중 손실이 가장 작은 안을 결정론 탐색으로 고르며, (c) 예산을 못 맞추면 무엇이 얼마나 모자란지 수치로 답합니다.
단말 사양과 레이어 프로파일을 받아 예산 안에 들어가는 비트 배분안과 남는 메모리·지연 여유를 터미널 표로 답하는 CLI 계산기입니다. 데이터는 고객사 추출본을 가정한 더미로 시작합니다.
심어 둔 실측 지연·품질과 계산기 추정치의 오차를 재고, 예산 불가일 때 무엇을 얼마나 완화하면 되는지 대안 수치를 함께 내는 터미널 출력입니다.
단말 여러 종과 모델 여러 개를 한 번에 돌려 조합별 가능·불가와 남는 여유를 한 표로 정리하고 CSV 로 내보내는 출력입니다.
단말 6종과 모델 3개를 한 명령으로 돌리면 18개 조합의 배분안과 예산 잔여가 몇 초 안에 터미널에 나오는 걸 시연합니다
레이어 24개 이하 케이스 20건은 완전탐색과 대조해 계산기가 고른 배분안이 전부 같은 답이었습니다. 계산이 결정론이라 같은 입력이면 같은 답이 나온다는 것까지만 보이는 수치입니다
일부러 만든 악성·결측 입력 30종(가용 메모리 메모 없음·int4 미지원 단말·민감도 결측·단말 표기 흔들림)을 넣어도 출력 형식(스키마)이 한 번도 안 깨집니다
2주차에 심어 둔 실측 지연과 계산기 추정 지연의 평균 절대 오차가 8.4% 였습니다. 단, 실측값도 작성자가 만든 합성 기준이라 '성능 검증'이 아니라 '데모 조건에서의 동작'임을 명시합니다
진짜 검증은 여기서 멈춥니다. 위 수치는 전부 작성자가 만든 더미데이터 자가채점이며, 실제 추정 오차는 고객사의 실 단말과 실 프로파일에 돌려야 측정 가능합니다. 이 계산기는 그 검증을 바로 돌릴 수 있는 상태로 만듭니다
수치는 합성 데이터 자가채점이라 성능 증명이 아닙니다. 면접에서는 데모 동작과 실검증 준비 상태로 솔직히 설명합니다
이 포트폴리오가 보여주는 것은 하드웨어 예산을 제약으로 놓고 비트 배분을 계산·탐색하는 최적화 엔지니어링입니다. 양자화 커널 구현·NPU 컴파일러 튜닝 같은 하드웨어 도메인 전문성은 포트폴리오로 증명되지 않으므로 주장하지 않으며, 모르는 깊이는 면접에서 모른다고 말하는 편이 낫습니다
지연과 품질 손실은 실측이 아니라 대역폭·민감도로 세운 추정식의 결과입니다. 추정치에 오차 범위를 함께 붙이고, 실측 없이는 탑재 가부를 단정하지 않습니다
정답을 작성자가 만들었으니 잘 맞는 것이 당연합니다. 예산 경계에 딱 걸치는 조합·int4 미지원 단말·민감도 결측을 더미에 일부러 섞어, 쉬운 케이스만 푸는 게 아님을 보여줍니다
단말 사양서 표기가 제각각이고 가용 메모리가 문장으로만 적힌 행이 있습니다. LLM 이 옮긴 숫자는 재검산으로 확인하고, 못 옮긴 건 추측하지 말고 '예산 미확정'으로 계산을 보류합니다. 실제 도입 시 칩별 대역폭 상수를 다시 맞춰야 합니다(도입 단계 과제로 분리)
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
고객사 하드웨어팀에게서 받은 단말 사양 추출본. 계산기가 예산을 세울 때 가장 먼저 읽는 표입니다. 가용 메모리가 문장 메모로만 적힌 행, int4 를 지원하지 않는 단말, 같은 단말의 표기 별칭을 일부러 섞어 파싱과 예산 확정이 쉬운 경우만 다루지 않게 합니다.
| device_id | device_raw | npu_mem_mb | bandwidth_gbs | supported_bits | latency_budget_ms | mem_share_note | usable_mem_mb |
|---|---|---|---|---|---|---|---|
| dv_001 | AX-100 (1세대) | 4,096 | 51.2 | ["int4","int8","fp16"] | 120 | 전체 4GB 중 앱 몫 45% | 1,843 |
| dv_002 | AX100 1세대 | 4,096 | 51.2 | ["int4","int8","fp16"] | 120 | 앱 몫 45% | 1,843 |
| dv_003 | AX-200 | 6,144 | 68 | ["int4","int8","fp16"] | 90 | 전체 6GB 중 앱 몫 50% | 3,072 |
| dv_004 | BX-50 저가형 | 2,048 | 25.6 | ["int8","fp16"] | 150 | 전체 2GB 중 앱 몫 40% | 819 |
| dv_005 | BX-50S | 2,048 | 25.6 | ["int8","fp16"] | 150 | — |
아래 Zod 스키마에 맞는 고객사 단말 사양 40행을 JSON 배열로 생성합니다. 단말 계열 5~7종에 개정판·별칭을 섞고, 웨어러블부터 차량용까지 메모리 1GB~8GB 범위로 분포시킵니다. usable_mem_mb 는 mem_share_note 의 비율을 npu_mem_mb 에 적용한 정수로 채우되, 메모가 비었거나 비율이 미정이면 null 로 둡니다.
{"type":"object","properties":{"device_id":{"type":"string"},"device_raw":{"type":"string"},"npu_mem_mb":{"type":"integer"},"bandwidth_gbs":{"type":"number"},"supported_bits":{"type":"array","items":{"type":"string","enum":["int4","int8","fp16"]}},"latency_budget_ms":{"type":"integer"},"mem_share_note":{"type":"string"},"usable_mem_mb":{"type":["integer","null"]}},"required":["device_id","device_raw","npu_mem_mb","bandwidth_gbs","supported_bits","latency_budget_ms","mem_share_note","usable_mem_mb"]}
쉬운 데이터만 만들지 말 것. 다음 하드 케이스를 의도적으로 섞어라(예산이 늘 깔끔하게 확정되는 함정 회피): (1) 같은 단말의 표기 별칭('AX-100 (1세대)'/'AX100 1세대'처럼 device_master 로 정규화돼야 함) 4건 이상, (2) mem_share_note 가 빈 문자열이거나 '앱 몫 미정'이라 usable_mem_mb 를 null 로 둬야 하는 행 3건, (3) 비율을 숫자가 아니라 문장으로 적은 행('앱 몫 절반', '3분의 1 정도') 3건, (4) int4 를 지원하지 않는 단말 5건 이상, (5) 어떤 배분으로도 예산을 못 맞추는 극소형 단말 2건. JSON 배열만.{"type":"object","properties":{"device_id":{"type":"string"},"device_raw":{"type":"string"},"npu_mem_mb":{"type":"integer"},"bandwidth_gbs":{"type":"number"},"supported_bits":{"type":"array","items":{"type":"string","enum":["int4","int8","fp16"]}},"latency_budget_ms":{"type":"integer"},"mem_share_note":{"type":"string"},"usable_mem_mb":{"type":["integer","null"]}},"required":["device_id","device_raw","npu_mem_mb","bandwidth_gbs","supported_bits","latency_budget_ms","mem_share_note","usable_mem_mb"]}모델에서 뽑은 레이어별 프로파일. 배분 탐색이 비용과 손실을 계산하는 근거 표입니다. gold_bits 에 완전탐색으로 구한 정답 배분을 심어 계산기의 답을 자가채점합니다. 민감도 결측과 최소 비트 제약이 걸린 레이어를 섞어 무조건 int4 로 밀어붙이지 못하게 합니다.
| row_id | model_id | layer | kind | params_m | outlier_ratio | sensitivity | min_bits | gold_bits |
|---|---|---|---|---|---|---|---|---|
| lp_001 | m_llama7b | embed | embed | 131 | 0.02 | 0.9 | int8 | int8 |
| lp_002 | m_llama7b | attn.0 | attn | 50.3 | 0.31 | 1.8 | int8 | fp16 |
| lp_003 | m_llama7b | mlp.12 | mlp | 90.2 | 0.04 | 0.3 | — | int4 |
| lp_004 | m_llama7b | attn.9 | attn | 50.3 | 0.07 | 0.5 | — | int4 |
| lp_005 | m_llama7b | head | head | 131 | 0.12 | 1.2 | int8 | int8 |
아래 Zod 스키마에 맞는 레이어 프로파일 80행을 JSON 배열로 생성합니다. 모델 4~6개, 모델마다 embed·head 각 1개와 attn·mlp 를 10개 이상 두고, params_m 은 embed·head 가 크고 attn 이 작게 잡습니다. gold_bits 는 '가용 메모리 1843MB·지연 예산 120ms' 기준 완전탐색으로 구한 손실 최소 배분을 적되, min_bits 제약과 outlier_ratio 0.25 이상이면 int4 로 내리지 않는 규칙을 지킵니다.
{"type":"object","properties":{"row_id":{"type":"string"},"model_id":{"type":"string"},"layer":{"type":"string"},"kind":{"type":"string","enum":["attn","mlp","embed","head"]},"params_m":{"type":"number"},"outlier_ratio":{"type":["number","null"]},"sensitivity":{"type":["number","null"]},"min_bits":{"type":["string","null"],"enum":["int4","int8","fp16"]},"gold_bits":{"type":"string","enum":["int4","int8","fp16"]}},"required":["row_id","model_id","layer","kind","params_m","outlier_ratio","sensitivity","min_bits","gold_bits"]}
쉬운 데이터만 만들지 말 것. 다음 하드 케이스를 의도적으로 섞어라(무조건 int4 로 밀면 되는 함정 회피): (1) outlier_ratio 가 0.25 를 넘어 fp16 으로 남겨야 하는 레이어 6건 이상, (2) sensitivity 또는 outlier_ratio 가 null 인 결측 행 4건(이 경우 min_bits 를 int8 로 올려 보수적으로 둔다), (3) 민감도가 0.30~0.35 로 int4 와 int8 의 손실 차가 거의 없는 경계 레이어 5건, (4) params_m 이 커서 한 단계만 내려도 메모리가 크게 흔들리는 레이어 3건, (5) min_bits 가 fp16 으로 못 박힌 레이어 2건. JSON 배열만.{"type":"object","properties":{"row_id":{"type":"string"},"model_id":{"type":"string"},"layer":{"type":"string"},"kind":{"type":"string","enum":["attn","mlp","embed","head"]},"params_m":{"type":"number"},"outlier_ratio":{"type":["number","null"]},"sensitivity":{"type":["number","null"]},"min_bits":{"type":["string","null"],"enum":["int4","int8","fp16"]},"gold_bits":{"type":"string","enum":["int4","int8","fp16"]}},"required":["row_id","model_id","layer","kind","params_m","outlier_ratio","sensitivity","min_bits","gold_bits"]}| Python | 사양·프로파일 파서와 예산 계산식·배분 탐색을 구현하는 언어입니다 |
| PyTorch | 레이어 파라미터 수와 활성값 이상치 비율을 뽑아내는 프로파일 추출 경로를 가정합니다 |
| HuggingFace | 프로파일 대상 모델과 민감도 측정용 캘리브레이션 세트를 불러오는 경로를 가정합니다 |
| ONNX | 레이어 그래프를 읽어 레이어 목록과 연산 종류를 맞추는 입력 형식입니다 |
| AIMET | 계산한 레이어별 비트 배분을 양자화 도구 설정으로 내보낼 때의 목표 형식입니다 |
| 딥러닝 | 비트 강등 민감도와 품질 손실 추정식을 세우는 판단 기준입니다 |
| Zod | 사양·프로파일 입력과 LLM 파싱 결과의 런타임 형식 검증입니다 |
| Claude | 사양서 자유 서식 메모를 숫자로 옮기고 배분 결과를 한 문단으로 설명하는 LLM 입니다 |
✓ pass → 그대로 멈춰도 완결입니다. 더 키우려면 2주차(추정치와 실측의 오차 확인)로 진행합니다.
✓ pass → 여기서 멈춰도 완결입니다. 더 키우면 3주차(단말·모델 조합 일괄 비교)로 진행합니다.
단말 1종과 모델 1개 조합의 배분 계산 결과. 배분안과 예산 잔여·불가 사유를 한 스키마로 담아 터미널 표와 CSV 에 씁니다. 탑재 확정이 아니라 예산 검토용 산출입니다.
{"type":"object","properties":{"device_id":{"type":"string"},"model_id":{"type":"string"},"feasible":{"type":"boolean"},"bits_by_layer":{"type":"array","items":{"type":"object","properties":{"layer":{"type":"string"},"bits":{"type":"string","enum":["int4","int8","fp16"]}},"required":["layer","bits"]}},"weight_mem_mb":{"type":"number"},"kv_cache_mem_mb":{"type":"number"},"mem_headroom_mb":{"type":"number"},"est_latency_ms":{"type":"number"},"est_quality_loss":{"type":"number"},"blockers":{"type":"array","items":{"type":"string","enum":["mem_over","latency_over","bits_unsupported","missing_budget"]}},"rationale":{"type":"string","minLength":20},"evidence":{"type":"string","minLength":8},"hedge":{"type":["string","null"]}},"required":["device_id","model_id","feasible","bits_by_layer","weight_mem_mb","kv_cache_mem_mb","mem_headroom_mb","est_latency_ms","est_quality_loss","blockers","rationale","evidence","hedge"]}[
{
"input_ref": "dv_001 (AX-100, 가용 1843MB · 지연 120ms) × m_llama7b",
"output": {
"device_id": "dv_001",
"model_id": "m_llama7b",
"feasible": true,
"bits_by_layer": [
{
"layer": "embed",
"bits": "int8"
},
{
"layer": "attn.0",
"bits": "fp16"
},
{
"layer": "mlp.12",
"bits": "int4"
},
{
"layer": "attn.9",
"bits": "int4"
},
{
"layer": "head",
"bits": "int8"
}
],
"weight_mem_mb": 432.9,
"kv_cache_mem_mb": 128,
"mem_headroom_mb": 1282.1,
"est_latency_ms": 84.2,
"est_quality_loss": 0.62,
"blockers": [],
"rationale": "가중치 432.9MB 와 KV 캐시 128.0MB 를 더해 560.9MB 를 쓰고 가용 1843MB 대비 1282.1MB 가 남습니다. 활성값 이상치 비율이 0.31 인 attn.0 만 fp16 으로 남기고 민감도가 낮은 mlp.12·attn.9 를 int4 로 내려 추정 지연 84.2ms 로 예산 120ms 안에 들어갑니다.",
"evidence": "usable_mem_mb=1843, weight_mem_mb=432.9, est_latency_ms=84.2, latency_budget_ms=120",
"hedge": null
}
},
{
"input_ref": "dv_006 (CX-10 웨어러블, 가용 358MB · int4/int8 만 지원) × m_llama7b",
"output": {
"device_id": "dv_006",
"model_id": "m_llama7b",
"feasible": false,
"bits_by_layer": [
{
"layer": "embed",
"bits": "int8"
},
{
"layer": "attn.0",
"bits": "int8"
},
{
"layer": "mlp.12",
"bits": "int4"
},
{
"layer": "attn.9",
"bits": "int4"
},
{
"layer": "head",
"bits": "int8"
}
],
"weight_mem_mb": 382.6,
"kv_cache_mem_mb": 96,
"mem_headroom_mb": -120.6,
"est_latency_ms": 141,
"est_quality_loss": 1.34,
"blockers": [
"mem_over"
],
"rationale": "가장 작게 잡을 수 있는 배분도 478.6MB 라 가용 358MB 를 120.6MB 넘습니다. embed·head·attn.0 은 최소 비트가 int8 이라 더 내릴 수 없어 이 단말에서는 현재 모델 구성으로 예산을 맞출 방법이 없습니다.",
"evidence": "usable_mem_mb=358, min_plan_mem_mb=478.6, mem_headroom_mb=-120.6",
"hedge": "부족분 120.6MB 는 컨텍스트 길이를 줄여 KV 캐시를 낮추는 방향으로 다시 계산해야 합니다"
}
},
{
"input_ref": "dv_007 (AX-300 시제품, 앱 몫 미정) × m_qwen4b",
"output": {
"device_id": "dv_007",
"model_id": "m_qwen4b",
"feasible": false,
"bits_by_layer": [],
"weight_mem_mb": 0,
"kv_cache_mem_mb": 0,
"mem_headroom_mb": 0,
"est_latency_ms": 0,
"est_quality_loss": 0,
"blockers": [
"missing_budget"
],
"rationale": "사양서에 앱 몫이 미정으로만 적혀 있어 가용 메모리를 확정하지 못했습니다. 예산이 서지 않으면 배분을 계산해도 의미가 없으므로 하드웨어팀에 앱 메모리 몫을 확인한 뒤 다시 돌립니다.",
"evidence": "mem_share_note=\"앱 몫 미정\", usable_mem_mb=null",
"hedge": "가용 메모리가 미확정이라 가능 여부를 추정하지 않습니다"
}
}
][자동 검증] 1. Zod parse 로 출력 형식(필드·enum)이 스키마를 통과하는지 확인합니다. 2. 레이어 24개 이하 케이스는 완전탐색과 대조해 같은 배분이 나오는지 봅니다. 3. weight_mem_mb·kv_cache_mem_mb·est_latency_ms 를 탐색기 밖에서 다시 계산해 1% 이내로 일치하는지 봅니다. 4. bits_by_layer 가 단말 supported_bits 와 레이어 min_bits 제약을 모두 지키는지 확인합니다. 5. usable_mem_mb 가 null 이면 feasible 을 단정하지 않고 missing_budget 으로 보류하며, LLM 이 쓴 rationale 의 숫자가 계산 필드와 일치하는지 대조합니다. [수동 검증] 1. 검토자 2명이 사양 메모의 수치화 결과를 비동기로 확인해 일치율과 bootstrap Cohen's κ(두 사람 일치가 우연 이상인 정도, 0~1) 하한 0.6 이상을 봅니다. 2. 전체의 5% 를 사람이 직접 점검합니다.