딥러닝 공부방입니다. 근데 이제 야매를 곁들인. https://zeroact.tistory.com/ 딥러닝 공부한 것을 누군가는 도움이 되겠지 하며 올립니다. https://github.com/ZeroAct ko Sun, 22 Mar 2026 19:14:54 +0900 TISTORY 100 ZeroAct 딥러닝 공부방입니다. 근데 이제 야매를 곁들인. https://tistory1.daumcdn.net/tistory/3387415/attach/613eb68539244f9fb7f1e8e88e522b5b https://zeroact.tistory.com FunctionGemma 엔지니어링 2 https://zeroact.tistory.com/42 <p data-ke-size="size16">지난 글</p> <p data-ke-size="size16"><a href="https://zeroact.tistory.com/41" target="_blank" rel="noopener&nbsp;noreferrer">https://zeroact.tistory.com/41</a></p> <figure id="og_1766818787153" contenteditable="false" data-ke-type="opengraph" data-ke-align="alignCenter" data-og-type="article" data-og-title="FunctionGemma 엔지니어링 1" data-og-description="변화가 너무나도 빠르게 진행되고 있다.26년에는 더 빠를 것으로 예상된다. 2025년 12월 18일 Google 에서 FunctionGemma라는 모델을 공개했다.Gemma3 모델 아키텍처를 그대로 사용한 것인데 일반적인 대화" data-og-host="zeroact.tistory.com" data-og-source-url="https://zeroact.tistory.com/41" data-og-url="https://zeroact.tistory.com/41" data-og-image="https://scrap.kakaocdn.net/dn/bSEdgN/hyZQxXgKnb/kmo8khC1MvSLTxKGF7NVyK/img.png?width=800&amp;height=800&amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/k9mIJ/hyZPV6ybcj/teO5WsvFVJ6Lrs5KcOsvpk/img.png?width=800&amp;height=800&amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/tPaGe/hyZQyok0Di/7nLSaDVEt4NawJ87NkGZp0/img.jpg?width=396&amp;height=398&amp;face=172_91_298_228"><a href="https://zeroact.tistory.com/41" target="_blank" rel="noopener" data-source-url="https://zeroact.tistory.com/41"> <div class="og-image" style="background-image: url('https://scrap.kakaocdn.net/dn/bSEdgN/hyZQxXgKnb/kmo8khC1MvSLTxKGF7NVyK/img.png?width=800&amp;height=800&amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/k9mIJ/hyZPV6ybcj/teO5WsvFVJ6Lrs5KcOsvpk/img.png?width=800&amp;height=800&amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/tPaGe/hyZQyok0Di/7nLSaDVEt4NawJ87NkGZp0/img.jpg?width=396&amp;height=398&amp;face=172_91_298_228');">&nbsp;</div> <div class="og-text"> <p class="og-title" data-ke-size="size16">FunctionGemma 엔지니어링 1</p> <p class="og-desc" data-ke-size="size16">변화가 너무나도 빠르게 진행되고 있다.26년에는 더 빠를 것으로 예상된다. 2025년 12월 18일 Google 에서 FunctionGemma라는 모델을 공개했다.Gemma3 모델 아키텍처를 그대로 사용한 것인데 일반적인 대화</p> <p class="og-host" data-ke-size="size16">zeroact.tistory.com</p> </div> </a></figure> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">FunctionGemma는 Google에서 공개한 Gemma3 모델이다. 근데 이제 Function Calling을 곁들인.</p> <p data-ke-size="size16">공식문서에서 말하고 있는 것처럼 그냥 쓰면 별 볼일 없는 모델이다.</p> <p data-ke-size="size16">근데 파인튜닝을 한다면 270m 라는 매우매우 작은 크기로도 Function Calling에 있어서는 매우 강력한 성능을 낼 수 있다.</p> <p data-ke-size="size16">&nbsp;</p> <h4 data-ke-size="size20">Function Calling</h4> <p data-ke-size="size16"><a title="google/mobile-actions" href="https://huggingface.co/datasets/google/mobile-actions" target="_blank" rel="noopener">https://huggingface.co/datasets/google/mobile-actions</a></p> <p data-ke-size="size16">해당 데이터셋은 도구 리스트, <span style="color: #333333; text-align: start;">유저 질문,<span>&nbsp;</span></span> 정답 도구 호출 스펙 총 세개로 구성되어 있다.</p> <p data-ke-size="size16">각각의 예시를 보면 단번에 Function Calling 을 이해할 수 있다.</p> <blockquote data-ke-style="style2">도구 리스트<br />- name: show_map, description: 지도 보여주기, parameter: location (위치)<br />- name: open_wifi_settings, description: wifi 설정 열기, paramter: -<br />- name: create_calendar_event, description: 달력 이벤트 생성하기, parameter: title (제목), datetime (일시)<br /><br />유저 질문<br />- 2025년 12월 27일 저녁 6시에 채현이와 저녁 약속 알람 맞춰줘.<br /><br />정답 도구 호출 스펙<br />- { "name": "create_calendar_event", "arguments": { "title": "채현 저녁 약속", "datetime": "2025-12-27T18:00:00", ... }</blockquote> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">LLM은 도구 리스트와 유저 질문을 입력으로 받고 필요한 도구 호출 스펙을 출력할 수 있는 능력이 있다.</p> <p data-ke-size="size16">이는 어디에 프로그래밍 되어 있는 것이 아니라 도구 호출 스펙도 텍스트(자연어)이기 때문에 구조화된 포맷으로 텍스트를 출력하게끔 학습하면 되는 것이다.</p> <p data-ke-size="size16">이 능력(?)은 tool calling, structured output, function calling 등 다양한 이름으로 불리는데 구글은 Function Calling이라는 용어를 쓰기로 한 것 같다.</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">공식문서에 있는 다음 그림을 보면 더 자세히 알 수 있다.</p> <p><figure class="imageblock alignCenter" data-ke-mobileStyle="widthOrigin" data-origin-width="865" data-origin-height="1214"><span data-url="https://blog.kakaocdn.net/dn/bFzhSd/dJMcaaDUDbP/BtKuTfAYzkcitM1DLLlJek/img.png" data-phocus="https://blog.kakaocdn.net/dn/bFzhSd/dJMcaaDUDbP/BtKuTfAYzkcitM1DLLlJek/img.png"><img src="https://blog.kakaocdn.net/dn/bFzhSd/dJMcaaDUDbP/BtKuTfAYzkcitM1DLLlJek/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFzhSd%2FdJMcaaDUDbP%2FBtKuTfAYzkcitM1DLLlJek%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="584" height="820" data-origin-width="865" data-origin-height="1214"/></span></figure> </p> <p data-ke-size="size16"><b>TURN1: DEVELOPER</b></p> <p data-ke-size="size16">- 도구를 언제 써야하는지 무슨 파라미터들이 있는지가 설명된 프롬프트를 준다.</p> <p data-ke-size="size16"><b>TURN2: USER</b></p> <p data-ke-size="size16">- 사용자의 요청을 입력 받는다.</p> <p data-ke-size="size16"><b>TURN3: MODEL</b></p> <p data-ke-size="size16">- 필요한 도구 호출 스펙을 출력한다.</p> <p data-ke-size="size16"><b>(APPLICATION LOGIC: System)</b></p> <p data-ke-size="size16">(- 도구 호출 스펙으로 실제 API 혹은 함수를 호출한다.)</p> <p data-ke-size="size16"><b>TURN4: DEVELOPER</b></p> <p data-ke-size="size16">- 프롬프트에 도구 호출 결과를 삽입한다.</p> <p data-ke-size="size16"><b>TURN5: MODEL</b></p> <p data-ke-size="size16">- TURN2, TURN4 각각의 요청과 도구 호출 결과를 사용해 응답을 생성한다.</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">실제 도구 호출이 어떤 타이밍에 어떻게 동작하는지 잘 이해할 수 있게 설명된 그림인 것 같다.</p> <p data-ke-size="size16">MCP를 사용하는 챗봇들도 다 이런 흐름으로 동작한다.</p> <p data-ke-size="size16">&nbsp;</p> <h4 data-ke-size="size20">FunctionGemma</h4> <p data-ke-size="size16">구글에서 발표한 FunctionGemma는 일반 대화를 위한 Gemma3 270M과 완벽하게 동일한 모델이지만 Function Calling 을 위해 학습된 모델이다.</p> <p data-ke-size="size16">모델 크기가 매우 작기때문에 대화까지 잘 하면서 Function Calling 까지 잘 하기는 어렵다.</p> <p data-ke-size="size16">때문에 의도적으로 용도를 분리한 것 같고 매우 효율적이며 미래 지향적 접근법인 것 같다.</p> <p data-ke-size="size16">(오히려 이걸 보여주려고 270M 모델을 출시한 것이 아닌가 하는 합리적 의심이 든다.)</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">공식 문서에서도 볼 수 있듯 General한 성능이 뛰어나지는 않다.</p> <p data-ke-size="size16">다만, 파인튜닝을 한다면 개인 디바이스에도 쉽게 올릴 수 있는 크기의 훌륭한 모델이 탄생한다.</p> <p><figure class="imageblock alignCenter" data-ke-mobileStyle="widthOrigin" data-origin-width="952" data-origin-height="403"><span data-url="https://blog.kakaocdn.net/dn/pdj9E/dJMcacaEXmk/8WB57uRohydOAFlVa9O3mk/img.png" data-phocus="https://blog.kakaocdn.net/dn/pdj9E/dJMcacaEXmk/8WB57uRohydOAFlVa9O3mk/img.png" data-alt="google/mobile-actions 데이터 셋 평가 결과"><img src="https://blog.kakaocdn.net/dn/pdj9E/dJMcacaEXmk/8WB57uRohydOAFlVa9O3mk/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fpdj9E%2FdJMcacaEXmk%2F8WB57uRohydOAFlVa9O3mk%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="602" height="255" data-origin-width="952" data-origin-height="403"/></span><figcaption>google/mobile-actions 데이터 셋 평가 결과</figcaption> </figure> </p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">아직 multi-step, multi-turn 등 모델 크기에서 오는 한계점들이 몇가지 있는데, 정말 순식간에 (어쩌면 2026년 상반기에) 다 해결될 문제들이기에 다루지 않겠다.</p> <p data-ke-size="size16">&nbsp;</p> <h4 data-ke-size="size20">Conclusion</h4> <p data-ke-size="size16">최근 Dify, n8n, Flowise 같은 Agent 생성 도구들을 다루고 있는데, 쿼리를 보고 Flow를 분기해야하는 경우나 내부 API 호출을 해야할 때 크기가 큰 일반 대화용 모델을 사용하는 것이 큰 걸림돌이었다.</p> <p data-ke-size="size16">비용적인 문제로 여러가지 모델을 올릴 수 없는 상황에 이런 용도로 다른 모델을 쓰자니 여전히 무겁고, gpt-oss 같은 성능 좋은 reasoning 모델을 쓰기에는 시간이 너무 오래걸리고 호환성도 떨어졌다.</p> <p style="color: #333333; text-align: start;" data-ke-size="size16">&nbsp;</p> <p style="color: #333333; text-align: start;" data-ke-size="size16">Function Calling은 무한한 가능성이 있는 LLM 활용 방법이다.</p> <p style="color: #333333; text-align: start;" data-ke-size="size16">예시처럼 휴대폰 API를 호출해서 정말 똑똑한 빅스비나 siri를 만들수도 있고,</p> <p style="color: #333333; text-align: start;" data-ke-size="size16">레거시 서비스의 API 문서를 학습시켜 손쉽게 Agentic AI 를 실현할 수도 있고,</p> <p style="color: #333333; text-align: start;" data-ke-size="size16">더 나아가 로봇 팔 제어 도구의 스펙을 학습시켜 팔을 움직이게 할 수도 있다.</p> <p style="color: #333333; text-align: start;" data-ke-size="size16">&nbsp;</p> <p style="color: #333333; text-align: start;" data-ke-size="size16">1편에서 말한 것처럼 단순한 AI 활용에서 AI 엔지니어링으로 전환되는 중요한 시기인 것 같다.</p> <p style="color: #333333; text-align: start;" data-ke-size="size16">다음 3편에서는 FunctionGemma 를 위한 커스텀 데이터 생성부터 직접 파인튜닝까지 해볼 예정이다.</p> 딥러닝/기타 Ai FunctionGemma Gemma GenAI llm MCP 구글 엔지니어 인공지능 ZeroAct https://zeroact.tistory.com/42 https://zeroact.tistory.com/42#entry42comment Sat, 27 Dec 2025 17:43:42 +0900 FunctionGemma 엔지니어링 1 https://zeroact.tistory.com/41 <p data-ke-size="size16">변화가 너무나도 빠르게 진행되고 있다.</p> <p data-ke-size="size16">26년에는 더 빠를 것으로 예상된다.</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">2025년 12월 18일 Google 에서 FunctionGemma라는 모델을 공개했다.</p> <p data-ke-size="size16">Gemma3 모델 아키텍처를 그대로 사용한 것인데 일반적인 대화가 아닌 Function Calling을 위한 용도로 재 출시를 한 것이다.</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">문서를 쭉 읽어보니 느껴진 것이 있다.</p> <p data-ke-size="size16">생성형 AI는 이제서야 엔지니어링 단계에 진입을 했다.</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">자원이 무한하면 그냥 제일 좋은 모델 가져다 사용하면 되는데, 보통은 그렇지 않다. (그리고... 돈은 아낄수록 좋다고 ㅎㅎ)</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">엔지니어는 기술을 이해하고 미래를 예측하며 엔지니어링을 해야한다고 생각한다.</p> <p data-ke-size="size16">새로운 기술이 나올때마다 놀라고 그때그때 변해야 하는 프로그램을 만드는 것은 엔지니어가 아니라......</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">FunctionGemma는 Agentic AI 를 넘어 Physical AI 시대를 이끌어가기 위해서 무조건 공부를 해야하는 대상이라는 생각이 들었다. (아마 비슷한 것들이 별의별 이름을 달면서 나오겠지..)</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">그래서, 이번 글을 시작으로 총 3개의 포스팅을 할 예정이다.</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">1. (이 글)</p> <p data-ke-size="size16">2. FunctionGemma가 뭔지, 왜 쓰는지</p> <p data-ke-size="size16">3. FunctionGemma 파인튜닝 -&gt; github 공개 예정</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">---</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">p.s 요즘 구글링하면 원하는 정보를 찾기가 너무 힘들다 ㅠ 같은 형식으로 LLM이 생성한 의미없는 포스트가 너무 많은 것 같다. AI에게 일자리를 빼앗기 걱정을 하면서 AI에게 셀프로 잠식당하는 모순이 안타깝다.&nbsp;<br /><br />다음 글</p> <p data-ke-size="size16"><a href="https://zeroact.tistory.com/42" target="_blank" rel="noopener&nbsp;noreferrer">https://zeroact.tistory.com/42</a></p> <figure id="og_1766825104172" contenteditable="false" data-ke-type="opengraph" data-ke-align="alignCenter" data-og-type="article" data-og-title="FunctionGemma 엔지니어링 2" data-og-description="지난 글https://zeroact.tistory.com/41 FunctionGemma 엔지니어링 1변화가 너무나도 빠르게 진행되고 있다.26년에는 더 빠를 것으로 예상된다. 2025년 12월 18일 Google 에서 FunctionGemma라는 모델을 공개했다.Gemma3 " data-og-host="zeroact.tistory.com" data-og-source-url="https://zeroact.tistory.com/42" data-og-url="https://zeroact.tistory.com/42" data-og-image="https://scrap.kakaocdn.net/dn/bNIePr/hyZP5Bh2cO/yayL5eMdqLV6ScIb5RQH11/img.png?width=800&amp;height=800&amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/e2NBE/hyZP4h6PO9/tbfE8KppPf2VedYjlXONM0/img.png?width=800&amp;height=800&amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/beVHKc/hyZPKC4wD8/inVijKOHNWhruBsnA50FpK/img.png?width=865&amp;height=1214&amp;face=0_0_865_1214"><a href="https://zeroact.tistory.com/42" target="_blank" rel="noopener" data-source-url="https://zeroact.tistory.com/42"> <div class="og-image" style="background-image: url('https://scrap.kakaocdn.net/dn/bNIePr/hyZP5Bh2cO/yayL5eMdqLV6ScIb5RQH11/img.png?width=800&amp;height=800&amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/e2NBE/hyZP4h6PO9/tbfE8KppPf2VedYjlXONM0/img.png?width=800&amp;height=800&amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/beVHKc/hyZPKC4wD8/inVijKOHNWhruBsnA50FpK/img.png?width=865&amp;height=1214&amp;face=0_0_865_1214');">&nbsp;</div> <div class="og-text"> <p class="og-title" data-ke-size="size16">FunctionGemma 엔지니어링 2</p> <p class="og-desc" data-ke-size="size16">지난 글https://zeroact.tistory.com/41 FunctionGemma 엔지니어링 1변화가 너무나도 빠르게 진행되고 있다.26년에는 더 빠를 것으로 예상된다. 2025년 12월 18일 Google 에서 FunctionGemma라는 모델을 공개했다.Gemma3</p> <p class="og-host" data-ke-size="size16">zeroact.tistory.com</p> </div> </a></figure> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">&nbsp;</p> 딥러닝/기타 Ai FunctionGemma Gemma GenAI llm 구글 엔지니어 인공지능 ZeroAct https://zeroact.tistory.com/41 https://zeroact.tistory.com/41#entry41comment Sat, 27 Dec 2025 15:44:08 +0900 [Database] Transaction https://zeroact.tistory.com/40 <p data-ke-size="size16">Transaction 이란 하나 또는 그 이상의 database operations 의 묶음을 의미한다.</p> <p data-ke-size="size16">이 묶음은 하나의 작업으로 수행된다.</p> <p data-ke-size="size16">Transaction 은 operation 들이 한번에 일관적으로 수행되는 것을 보장하고 error 가 났을 때 operation 하나하나의 결과 상태가 아닌 transaction 수행 전의 상태로 돌아갈 수 있도록 rollback 할 수 있다.</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">4 가지 주요 원칙이 있는데 앞글자를 따서 ACID 라고 부른다.</p> <ol style="list-style-type: decimal;" data-ke-list-type="decimal"> <li><u><b>A</b></u>tomicity: 여러 명령들이 나눠질 수 없는 하나의 작업으로 취급된다. 성공하면 모든 것이 성공하는 것이고 실패한다면 모든 것이 실패하는 것이다.</li> <li><u><b>C</b></u>onsistency: 모든 데이터는 정의된 규칙에 의해 일관적이어야 한다.</li> <li><u><b>I</b></u>solation: 한 transaction 은 다른 transaction 들과 독립적이어야 하며 서로 영향을 주지 못해야 한다.</li> <li><u><b>D</b></u>urability: db 밖에서 무슨일이 있더라도 데이터는 안전해야 한다.</li> </ol> <p data-ke-size="size16">&nbsp;</p> 지식/Python acid Database Python SQL transaction ZeroAct https://zeroact.tistory.com/40 https://zeroact.tistory.com/40#entry40comment Thu, 24 Aug 2023 09:58:54 +0900 [Database] #1 SQL? https://zeroact.tistory.com/39 <p data-ke-size="size16"><a href="https://www.youtube.com/watch?v=HXV3zeQKqGY">SQL Tutorial - Full Database Course for Beginners - YouTube</a></p> <figure data-ke-type="video" data-ke-style="alignCenter" data-video-host="youtube" data-video-url="https://www.youtube.com/watch?v=HXV3zeQKqGY" data-video-thumbnail="https://scrap.kakaocdn.net/dn/bDnYHl/hySlNOCRLY/vzrXoxqGTP6IgABlFbEgbk/img.jpg?width=1280&amp;height=720&amp;face=0_0_1280_720" data-video-width="860" data-video-height="484" data-video-origin-width="860" data-video-origin-height="484" data-ke-mobilestyle="widthContent" data-original-url="" data-video-title=""><iframe src="https://www.youtube.com/embed/HXV3zeQKqGY" width="860" height="484" frameborder="" allowfullscreen="true"></iframe> <figcaption style="display: none;"></figcaption> </figure> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">모든 것이 데이터..</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">DBMS</p> <p data-ke-size="size16">- A special software program that helps users create and maintain a database</p> <p data-ke-size="size16">- create, read, update, delete</p> <p data-ke-size="size16">- user tell dbms to do those thing&nbsp;</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">Relational Database (SQL)</p> <p data-ke-size="size16">- Organize data into one ore more tables</p> <p data-ke-size="size16">&nbsp; - each table has columns and rows</p> <p data-ke-size="size16">&nbsp; - A unique key identifies each row</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">Non-Relational (no SQL / not just SQL)</p> <p data-ke-size="size16">- Organize data is anything but a traditional table</p> <p data-ke-size="size16">&nbsp; - Key-value stores</p> <p data-ke-size="size16">&nbsp; - Documents (JSON, BLOB, ..)</p> <p data-ke-size="size16">&nbsp; - Graphs</p> <p data-ke-size="size16">&nbsp; - Flexibel Tables</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">Relational Database Management Systems (RDBMS)</p> <p data-ke-size="size16">- mySQL, Oracle, postgreSQL, mariaDB</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">Non-Relational Database Management Systems (NRDBMS)</p> <p data-ke-size="size16">- mongoDB, dynamoDB, apache cassandra, firebase ...</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">Structured Query Language (SQL)</p> <p data-ke-size="size16">- Standardized language for interacting with RDBMS</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">primary key (unique id)</p> <p data-ke-size="size16">- should be unique in a table</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">foreign key</p> <p data-ke-size="size16">- stores ids of other tables</p> <p><figure class="imageblock alignCenter" data-ke-mobileStyle="widthOrigin" data-origin-width="1056" data-origin-height="254"><span data-url="https://blog.kakaocdn.net/dn/cMr145/btsbSgroUhP/gH6z1fsCJjBlqlza7fNco1/img.png" data-phocus="https://blog.kakaocdn.net/dn/cMr145/btsbSgroUhP/gH6z1fsCJjBlqlza7fNco1/img.png"><img src="https://blog.kakaocdn.net/dn/cMr145/btsbSgroUhP/gH6z1fsCJjBlqlza7fNco1/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcMr145%2FbtsbSgroUhP%2FgH6z1fsCJjBlqlza7fNco1%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="1056" height="254" data-origin-width="1056" data-origin-height="254"/></span></figure> </p> <p data-ke-size="size16">composite key</p> <p data-ke-size="size16">- need two attribute.</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">Structured Query language (SQL)</p> <p data-ke-size="size16">- is a language used for interacting with RDBMS</p> <p data-ke-size="size16">- implementations can be vary between systems</p> <p data-ke-size="size16">- can do 4 types of roles</p> <p data-ke-size="size16">&nbsp; - Data Query Language</p> <p data-ke-size="size16">&nbsp; - Data Definition Language</p> <p data-ke-size="size16">&nbsp; - Data Control Language</p> <p data-ke-size="size16">&nbsp; - Data Manipulation Language</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">Queries</p> <p data-ke-size="size16">- a set of instructions given to RDBMS</p> <p data-ke-size="size16">- Goal is to only get the data you need</p> 지식 ZeroAct https://zeroact.tistory.com/39 https://zeroact.tistory.com/39#entry39comment Mon, 24 Apr 2023 19:24:01 +0900 [Active Learning] Lightly ai, Active Learning 이란? https://zeroact.tistory.com/38 <p data-ke-size="size16">본 포스팅은 lightly ai 블로그 글의 요약 + 사견을 담고 있습니다.</p> <p data-ke-size="size16"><a href="https://www.lightly.ai/post/active-learning-method-overview">An overview of Active Learning methods (lightly.ai)</a></p> <figure id="og_1677631555777" contenteditable="false" data-ke-type="opengraph" data-ke-align="alignCenter" data-og-type="website" data-og-title="An overview of Active Learning methods" data-og-description="Learn about the advantages and disadvantages of Active Learning methods and which of them solves your problem." data-og-host="www.lightly.ai" data-og-source-url="https://www.lightly.ai/post/active-learning-method-overview" data-og-url="https://www.lightly.ai/post/active-learning-method-overview" data-og-image="https://scrap.kakaocdn.net/dn/l5vHn/hyRNOr1hEj/uFhkSHtflTw6Q7sxIZPrn0/img.png?width=1280&amp;height=832&amp;face=0_0_1280_832"><a href="https://www.lightly.ai/post/active-learning-method-overview" target="_blank" rel="noopener" data-source-url="https://www.lightly.ai/post/active-learning-method-overview"> <div class="og-image" style="background-image: url('https://scrap.kakaocdn.net/dn/l5vHn/hyRNOr1hEj/uFhkSHtflTw6Q7sxIZPrn0/img.png?width=1280&amp;height=832&amp;face=0_0_1280_832');">&nbsp;</div> <div class="og-text"> <p class="og-title" data-ke-size="size16">An overview of Active Learning methods</p> <p class="og-desc" data-ke-size="size16">Learn about the advantages and disadvantages of Active Learning methods and which of them solves your problem.</p> <p class="og-host" data-ke-size="size16">www.lightly.ai</p> </div> </a></figure> <h2 data-ke-size="size26">Why Active Learning?</h2> <p data-ke-size="size16">라벨링에는 돈과 시간이 든다.</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">요즘엔 데이터를 수집할 수 있는 환경이 아주 잘 구축 되어있다.</p> <p data-ke-size="size16">하지만, 그 데이터를 전부 라벨링하고 저장하고 관리한다는 것은 현실적으로 불가능하다.</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">그래서 수집된 상태의 데이터, 즉, Unlabeld Data를 모두 사용하지 않고</p> <p data-ke-size="size16">모델이 최고 성능을 낼 수 있는 Subset을 고르는 것이 Active Learning 의 역할이다.</p> <h2 data-ke-size="size26">Research Directions</h2> <p><figure class="imageblock alignCenter" data-ke-mobileStyle="widthOrigin" data-filename="63ee2842407e302ff5ff7384_dark.png" data-origin-width="1280" data-origin-height="832"><span data-url="https://blog.kakaocdn.net/dn/cNTKKp/btr1bkYuWMr/CfUProJkNp7HkFgGTOSGs1/img.png" data-phocus="https://blog.kakaocdn.net/dn/cNTKKp/btr1bkYuWMr/CfUProJkNp7HkFgGTOSGs1/img.png" data-alt="Overview of Active learning methods (lightly.ai)"><img src="https://blog.kakaocdn.net/dn/cNTKKp/btr1bkYuWMr/CfUProJkNp7HkFgGTOSGs1/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcNTKKp%2Fbtr1bkYuWMr%2FCfUProJkNp7HkFgGTOSGs1%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="1280" height="832" data-filename="63ee2842407e302ff5ff7384_dark.png" data-origin-width="1280" data-origin-height="832"/></span><figcaption>Overview of Active learning methods (lightly.ai)</figcaption> </figure> </p> <p data-ke-size="size16">내가 좋아하는 기업중 하나인 lightly ai 에서 이렇게 research directions 을 그려주셨다.</p> <p data-ke-size="size16">이 분야를 처음 접하지만 대충 어떤 연구들이 진행되었고 진행중인지 쉽게 파악할 수 있었다.</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">모든 기법의 상세 내용은 위에 링크를 참고 하시면 좋을 것 같고 큰 갈래에 대한 요약만 합니다.</p> <h3 data-ke-size="size23">1. Task-dependent active learning</h3> <p><figure class="imageblock alignCenter" data-ke-mobileStyle="widthOrigin" data-origin-width="796" data-origin-height="231"><span data-url="https://blog.kakaocdn.net/dn/qp5Io/btr1udpPhpK/MYtvzopxN2Msy7DlHJNaVk/img.png" data-phocus="https://blog.kakaocdn.net/dn/qp5Io/btr1udpPhpK/MYtvzopxN2Msy7DlHJNaVk/img.png"><img src="https://blog.kakaocdn.net/dn/qp5Io/btr1udpPhpK/MYtvzopxN2Msy7DlHJNaVk/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fqp5Io%2Fbtr1udpPhpK%2FMYtvzopxN2Msy7DlHJNaVk%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="796" height="231" data-origin-width="796" data-origin-height="231"/></span></figure> </p> <p data-ke-size="size16">task 종속적인 active learning 은 task 와 class 가 정해져 있는 경우이다.</p> <p data-ke-size="size16">그림에서 볼 수 있듯 이 갈래의 기법들은 loss 나 prediction 을 사용하기 때문에 초기 모델이 필요하다.</p> <p data-ke-size="size16">초기 모델을 학습 하기 위해서 전체 데이터의 일부분을 랜덤 샘플링해서 학습하는 과정이 필요하다.</p> <h4 data-ke-size="size20">Using prediction probabilities (모델 예측값 사용하기)</h4> <p data-ke-size="size16">전체 unlabeled 데이터들에 대한 prediction 값을 얻은 후, uncertainty 를 통해 selection 을 수행하는 방법이다.</p> <p data-ke-size="size16">여러 방법이 있지만 공통적인 철학은 동일하다.</p> <p><figure class="imageblock alignCenter" data-ke-mobileStyle="widthOrigin" data-filename="63ecf71191318599460c162c_Illust 3.webp" data-origin-width="1400" data-origin-height="753"><span data-url="https://blog.kakaocdn.net/dn/VtyKL/btr1nvSavzH/B90Z21ZUFFwK8OqU1OJwp1/img.webp" data-phocus="https://blog.kakaocdn.net/dn/VtyKL/btr1nvSavzH/B90Z21ZUFFwK8OqU1OJwp1/img.webp" data-alt="Example showing how uncertainty sampling leads to a better decision border (from&amp;amp;nbsp; Active Learning Literature Survey )"><img src="https://blog.kakaocdn.net/dn/VtyKL/btr1nvSavzH/B90Z21ZUFFwK8OqU1OJwp1/img.webp" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVtyKL%2Fbtr1nvSavzH%2FB90Z21ZUFFwK8OqU1OJwp1%2Fimg.webp" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="1400" height="753" data-filename="63ecf71191318599460c162c_Illust 3.webp" data-origin-width="1400" data-origin-height="753"/></span><figcaption>Example showing how uncertainty sampling leads to a better decision border (from&amp;nbsp; Active Learning Literature Survey )</figcaption> </figure> </p> <p data-ke-size="size16">모델이 어려워 하는 데이터, 즉, decision boundary 에 가까운 데이터를 골라서 모델을 학습하는 것이다.</p> <p data-ke-size="size16">이렇게 sampling 된 데이터를 hard samples 라고 한다.</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">하지만, hard sample 은 성능을 저하 시키는 여러 요인이 있다.</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">첫번째,</p> <p data-ke-size="size16">어려운 데이터일수록 사람이 보기에도 애매한 데이터 일 수 있고,</p> <p data-ke-size="size16">그렇기에 label noise나 정말 미묘한 차이에 민감해 질 수 있다.</p> <p data-ke-size="size16">또한, boundary 주변의 데이터만 학습하게 되면 high-signal feature 를 놓칠 수 있게 된다.</p> <p data-ke-size="size16">(high-signal, 정보량이 많은, 목적을 위해 꼭 필요한, 중요한 정도로 해석하면 된다.)</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">lightly 에서 이를 잘 표현하는 그림을 그려주었다.</p> <p><figure class="imagegridblock"> <div class="image-container"><span data-url="https://blog.kakaocdn.net/dn/sDwYf/btr1lvrlRSj/xBUcHV10TmwftcMP9zKzzK/img.webp" data-phocus="https://blog.kakaocdn.net/dn/sDwYf/btr1lvrlRSj/xBUcHV10TmwftcMP9zKzzK/img.webp" data-is-animation="false" data-origin-width="640" data-origin-height="480" data-filename="63ecf73536bccb38d818dd52_Ilustr 4.webp" style="width: 49.4186%; margin-right: 10px;" data-widthpercent="50"><img src="https://blog.kakaocdn.net/dn/sDwYf/btr1lvrlRSj/xBUcHV10TmwftcMP9zKzzK/img.webp" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsDwYf%2Fbtr1lvrlRSj%2FxBUcHV10TmwftcMP9zKzzK%2Fimg.webp" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="640" height="480"/></span><span data-url="https://blog.kakaocdn.net/dn/NMbgb/btr1dcTuao7/GKyBntsVv1KTbzte6p5nN0/img.webp" data-phocus="https://blog.kakaocdn.net/dn/NMbgb/btr1dcTuao7/GKyBntsVv1KTbzte6p5nN0/img.webp" data-is-animation="false" data-origin-width="640" data-origin-height="480" data-filename="63ecf755884dfdaf73639224_Illust 5.webp" style="width: 49.4186%;" data-widthpercent="50"><img src="https://blog.kakaocdn.net/dn/NMbgb/btr1dcTuao7/GKyBntsVv1KTbzte6p5nN0/img.webp" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNMbgb%2Fbtr1dcTuao7%2FGKyBntsVv1KTbzte6p5nN0%2Fimg.webp" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="640" height="480"/></span></div> <figcaption>좌) Uncertainty 높은 것 위주로 샘플링, 우) 골고루 샘플링 (lightly ai)</figcaption> </figure> </p> <h4 data-ke-size="size20">Bayesian method &amp; Loss learning</h4> <p data-ke-size="size16">두번째,</p> <p data-ke-size="size16">deep learning 모델은 자기 자신의 uncertainty 를 제대로 평가하지 못한다.</p> <p data-ke-size="size16">익히 알고 있는 overconfident 문제와 비슷한 맥락인 것 같다.</p> <p data-ke-size="size16">그래서 모델의 plain prediction 을 사용하지 않고 loss 를 사용하는 기법이 등장했다.</p> <p><figure class="imageblock alignCenter" data-ke-mobileStyle="widthOrigin" data-filename="63ecf77e51f18a868bfd7747_Illustr 6.webp" data-origin-width="642" data-origin-height="210"><span data-url="https://blog.kakaocdn.net/dn/U9qye/btr0VKqf3t0/fnyi86TIDxAKr2spVyhrOK/img.webp" data-phocus="https://blog.kakaocdn.net/dn/U9qye/btr0VKqf3t0/fnyi86TIDxAKr2spVyhrOK/img.webp" data-alt="Architecture of&amp;amp;nbsp; Learning Loss for Active Learning (2019)"><img src="https://blog.kakaocdn.net/dn/U9qye/btr0VKqf3t0/fnyi86TIDxAKr2spVyhrOK/img.webp" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FU9qye%2Fbtr0VKqf3t0%2Ffnyi86TIDxAKr2spVyhrOK%2Fimg.webp" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="642" height="210" data-filename="63ecf77e51f18a868bfd7747_Illustr 6.webp" data-origin-width="642" data-origin-height="210"/></span><figcaption>Architecture of&amp;nbsp; Learning Loss for Active Learning (2019)</figcaption> </figure> </p> <p data-ke-size="size16">uncertainty 자체를 학습하는 개념이다.</p> <p data-ke-size="size16">(자세한 내용은 생략, 대단하신 한국분이 쓰신 논문이라 반가웠다. 루닛)</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">Drop out 을 통해 variance 를 uncertainty 로 간주하는 BALD(2017) 라는 논문도 있다.</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">아쉽지만 당연하게도 이 방법들도 완벽하지는 않다.</p> <p data-ke-size="size16">추후 이런 문제를 커버하는 최신 논문을 리뷰해 볼 예정이다.</p> <h2 data-ke-size="size26">2. Task-independent active learning</h2> <p><figure class="imageblock alignCenter" data-ke-mobileStyle="widthOrigin" data-origin-width="794" data-origin-height="400"><span data-url="https://blog.kakaocdn.net/dn/bFOyLH/btr07zPe4Up/K9NvZ8p4uGhmWJihNXf2Ck/img.png" data-phocus="https://blog.kakaocdn.net/dn/bFOyLH/btr07zPe4Up/K9NvZ8p4uGhmWJihNXf2Ck/img.png"><img src="https://blog.kakaocdn.net/dn/bFOyLH/btr07zPe4Up/K9NvZ8p4uGhmWJihNXf2Ck/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFOyLH%2Fbtr07zPe4Up%2FK9NvZ8p4uGhmWJihNXf2Ck%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="794" height="400" data-origin-width="794" data-origin-height="400"/></span></figure> </p> <p data-ke-size="size16">task 독립적인 기법들은 task 와 class 가 모두 정해지지 않은 상태에서 사용할 수 있는 기법들이다.</p> <p data-ke-size="size16">벌써 막연한 느낌이 들지만 무엇이 있는지 보자.</p> <h4 data-ke-size="size20">Metadata-based active learning</h4> <p data-ke-size="size16">이 방법은 딥러닝을 사용한 것은 아니다.</p> <p data-ke-size="size16">말 그대로 Metadata (촬영된 시간, 날짜, 선명도, 휘도 따위) 를 통해 sampling 을 하는 방법이다.</p> <p data-ke-size="size16">이 방법으로 실무 문제에 적용한 적이 있는데 나이브 하지만 꽤 좋은 방법이다.</p> <h4 data-ke-size="size20">Embedding-based active learning</h4> <p data-ke-size="size16">Self supervised learning 을 통해 feature 를 잘 뽑는 모델을 학습하고 feature embedding 을 사용해 sampling 하는 방법이다.</p> <p data-ke-size="size16">이후의 방법은 uncertainty 기반 방법과 크게 다른 것 같지는 않다.</p> <h4 data-ke-size="size20">Diversity Selection</h4> <p data-ke-size="size16">마찬가지로 embedding 을 사용한 방법으로 최대한 다양한 feature를 가지는 데이터를 뽑는 방법이다.</p> <p data-ke-size="size16">일반적으로 넓은 커버리지를 가지는 데이터셋을 구축하는 것이 좋다.</p> <p data-ke-size="size16">랜덤 샘플링이 성능이 꽤나 좋은 이유가 이 때문이라고 생각된다.</p> <h4 data-ke-size="size20">Representative Selection</h4> <p data-ke-size="size16">엣지 케이스를 무시하고 주요 대상들을 잘 예측하는 것이 중요한 케이스에 사용하는 방법이다.</p> <p data-ke-size="size16">unlabeled 에서 비슷한(대표되는) 데이터를 뽑는다.</p> <h2 data-ke-size="size26">결론</h2> <p data-ke-size="size16"><span style="background-color: #ffffff; color: #1a1b1f;">The dream team when it comes to combinations are Uncertainty Selection and Diversity Selection.</span></p> <p data-ke-size="size16"><span style="background-color: #ffffff; color: #1a1b1f;">여러가지 방법을 잘 섞어 사용하는 것이 좋다.</span></p> 딥러닝/기타 active Ai big data Deep labeling Learning lightly unlabeled 인공지능 ZeroAct https://zeroact.tistory.com/38 https://zeroact.tistory.com/38#entry38comment Wed, 1 Mar 2023 12:16:06 +0900 [python] Factory Pattern https://zeroact.tistory.com/37 <h2 data-ke-size="size26">개요</h2> <p data-ke-size="size16">메이플스토리에 나오는 주황버섯 클래스를 만들어보자.</p> <pre id="code_1677354782101" class="python" data-ke-language="python" data-ke-type="codeblock"><code>class OrangeMushroom: def __init__(self): self.max_hp: int = 50 self.hp: int = self.max_hp def hit(self, damage: int): self.hp -= damage def is_dead(self) -&gt; bool: return self.hp &lt;= 0</code></pre> <p data-ke-size="size16">체력 관련된 method 들만 구현했다.</p> <p data-ke-size="size16">hit: 맞을 때 체력 깎기</p> <p data-ke-size="size16">is_dead: 죽었는지 살았는지</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">슬라임도 만들어보자</p> <pre id="code_1677354850269" class="python" data-ke-language="python" data-ke-type="codeblock"><code>class Slime: def __init__(self): self.max_hp: int = 20 self.hp: int = self.max_hp def hit(self, damage: int): self.hp -= damage def is_dead(self) -&gt; bool: return self.hp &lt;= 0</code></pre> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">모든 몬스터는 hit 과 is_dead 함수가 필요하다.</p> <p data-ke-size="size16">몬스터라는 클래스를 만들어 상속받자.</p> <pre id="code_1677355025928" class="python" data-ke-language="python" data-ke-type="codeblock"><code>class Monster: def __init__(self, max_hp: int): self.max_hp = max_hp self.hp= self.max_hp def hit(self, damage: int): self.hp -= damage def is_dead(self) -&gt; bool: return self.hp &lt;= 0 class OrangeMushroom(Monster): def __init__(self): super().__init__(50) class Slime(Monster): def __init__(self): super().__init__(20)</code></pre> <p data-ke-size="size16">여기까지는 아주 단순한 상속에 대한 예시이다.</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">이제 주황버섯 5, 슬라임 3마리 정도 생성해보자.</p> <pre id="code_1677355143262" class="python" data-ke-language="python" data-ke-type="codeblock"><code>monsters_to_create = { "OrangeMushroom": 5, "Slime": 3, } monsters = [] for monster_name, num in monsters_to_create.items(): if monster_name == "OrangeMushroom": monsters.extend([OrangeMushroom() for _ in range(num)]) elif monster_name == "Slime": monsters.extend([Slime() for _ in range(num)])</code></pre> <p data-ke-size="size16">뭐 이렇게 짜면 될 것 같다.</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">하지만 이게 메인 로직이면 많이 불편해진다.</p> <p data-ke-size="size16">몬스터 종류 만큼 메인 코드가 길어질 것 이다.</p> <p data-ke-size="size16">몬스터 이름을 받고 그에 맞는 몬스터를 생성해주는 함수가 있으면 좋을 것 같다.</p> <pre id="code_1677356317156" class="python" data-ke-language="python" data-ke-type="codeblock"><code>class MonsterFactory: def create_monster(self, monster_name): if monster_name == "OrangeMushroom": return OrangeMushroom() elif monster_name == "Slime": return Slime() monsters_to_create = { "OrangeMushroom": 5, "Slime": 3, } monsters = [] for monster_name, num in monsters_to_create.items(): monsters.extend([MonsterFactory.create_monster(monster_name=monster_name) for _ in range(num)])</code></pre> <p data-ke-size="size16">이것이 Factory 패턴이다.</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">만약,</p> <p data-ke-size="size16">리본돼지 클래스가 추가된다면 Factory 내의 create_monster 함수만 수정이되고,</p> <p data-ke-size="size16">메인 로직은 그대로 유지된다.</p> <h2 data-ke-size="size26">응용</h2> <p data-ke-size="size16">디자인 패턴은 정답이 없다.</p> <p data-ke-size="size16">기본적인 철학이 통한다면 어떤 다른 형태여도 상관이 없다.</p> <p data-ke-size="size16">위의 예시는 가장 기본적인 Factory 구조이다.</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">Bounding Box 클래스를 만들어 보았다.</p> <pre id="code_1677357345245" class="python" data-ke-language="python" data-ke-type="codeblock"><code>class BoundingBox: def __init__(self, xywh: list[int]): """Bounding Box Args: xywh (list[int]): left, top, width, height """ self.x1, self.y1, self.w, self.h = xywh self.x2 = self.x1 + self.w self.y2 = self.y1 + self.h def area(self) -&gt; int: return self.w * self.h def aspect_ratio(self) -&gt; float: return self.h / self.w</code></pre> <p data-ke-size="size16">xywh 를 인자로 받고</p> <p data-ke-size="size16">area, aspect_ratio 메소드가 구현되어 있다.</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">근데 만약에 내가 가지고 있는 데이터가 cxcywh 라고 해보자.</p> <pre id="code_1677357441275" class="python" data-ke-language="python" data-ke-type="codeblock"><code>BoundingBox([cx - w/2, cy - h/2, w, h])</code></pre> <p data-ke-size="size16">뭐 이런식으로 인자를 넘겨주면 될 것이다.</p> <p data-ke-size="size16">하지만, 코드가 더러워진다.</p> <p data-ke-size="size16">x1y1x2y2 로 가지고 있으면 메인 코드에 또 저런 코드를 짜야한다.</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">Factory 철학을 활용해보자.</p> <pre id="code_1677357606279" class="python" data-ke-language="python" data-ke-type="codeblock"><code>class BoundingBox: def __init__(self, xywh: list[int]): """Bounding Box Args: xywh (list[int]): left, top, width, height """ self.x1, self.y1, self.w, self.h = xywh self.x2 = self.x1 + self.w self.y2 = self.y1 + self.h @classmethod def from_xyxy(cls, xyxy: list[int]): x1, y1, x2, y2 = xyxy return cls([x1, y1, x2-x1, y2-y1]) @classmethod def from_cxcywh(cls, cxcywh: list[int]): cx, cy, w, h = cxcywh return cls([cx - w/2, cy - h/2, w, h]) BoundingBox.from_xyxy(x1y1x2y2) BoundingBox.from_cxcywh(cxcywh)</code></pre> <p data-ke-size="size16">이게 가장 좋은 예시인지는 모르겠으나 개념을 알고 있다면 사용할 수 있는 곳이 많다.</p> <h2 data-ke-size="size26">Ref.</h2> <p data-ke-size="size16"><a href="https://refactoring.guru/">Refactoring and Design Patterns</a></p> <figure id="og_1677357731688" contenteditable="false" data-ke-type="opengraph" data-ke-align="alignCenter" data-og-type="website" data-og-title="Refactoring and Design Patterns" data-og-description="Hello, world! Refactoring.Guru makes it easy for you to discover everything you need to know about refactoring, design patterns, SOLID principles, and other smart programming topics. This site shows you the big picture, how all these subjects intersect, wo" data-og-host="refactoring.guru" data-og-source-url="https://refactoring.guru/" data-og-url="https://refactoring.guru/" data-og-image="https://scrap.kakaocdn.net/dn/MeZUo/hyRJHVYJso/nQN0k5eqL34kiiUUYxUR51/img.png?width=476&amp;height=249&amp;face=0_0_476_249,https://scrap.kakaocdn.net/dn/bmhTih/hyRKYofK51/OsVmohKfGQijfHfVAVmpJK/img.png?width=466&amp;height=200&amp;face=0_0_466_200,https://scrap.kakaocdn.net/dn/q2Qcx/hyRKPdLH6d/Uuj3Oeow8EBtOpwnJu2rs0/img.png?width=200&amp;height=242&amp;face=0_0_200_242"><a href="https://refactoring.guru/" target="_blank" rel="noopener" data-source-url="https://refactoring.guru/"> <div class="og-image" style="background-image: url('https://scrap.kakaocdn.net/dn/MeZUo/hyRJHVYJso/nQN0k5eqL34kiiUUYxUR51/img.png?width=476&amp;height=249&amp;face=0_0_476_249,https://scrap.kakaocdn.net/dn/bmhTih/hyRKYofK51/OsVmohKfGQijfHfVAVmpJK/img.png?width=466&amp;height=200&amp;face=0_0_466_200,https://scrap.kakaocdn.net/dn/q2Qcx/hyRKPdLH6d/Uuj3Oeow8EBtOpwnJu2rs0/img.png?width=200&amp;height=242&amp;face=0_0_200_242');">&nbsp;</div> <div class="og-text"> <p class="og-title" data-ke-size="size16">Refactoring and Design Patterns</p> <p class="og-desc" data-ke-size="size16">Hello, world! Refactoring.Guru makes it easy for you to discover everything you need to know about refactoring, design patterns, SOLID principles, and other smart programming topics. This site shows you the big picture, how all these subjects intersect, wo</p> <p class="og-host" data-ke-size="size16">refactoring.guru</p> </div> </a></figure> <p data-ke-size="size16">&nbsp;</p> 지식/Python Factory Guru Pattern Python refactoring 파이썬 패턴 팩토리 ZeroAct https://zeroact.tistory.com/37 https://zeroact.tistory.com/37#entry37comment Sun, 26 Feb 2023 05:42:16 +0900 [python] 시간, 타입 체크 데코레이터 https://zeroact.tistory.com/36 <h2 data-ke-size="size26">개요</h2> <p data-ke-size="size16">파이썬에는 데코레이터라(decorator)는 것이 있다.</p> <p data-ke-size="size16">직역하면 꾸미다 라는 뜻인데 말그대로 함수를 꾸미는 것이다.</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">만약 함수의 시간을 체크 하고 싶다고 해보자.</p> <p data-ke-size="size16">그럼 이렇게 할 것 이다.</p> <pre id="code_1677349559351" class="python" data-ke-language="python" data-ke-type="codeblock"><code>import time import random def func(): time.sleep(random.random() * 1) start = time.time() func() ellapsed_time = time.time() - start print(f"Ellapsed Time: {ellapsed_time:.2f} seconds") # Ellapsed Time: 0.55 seconds</code></pre> <p data-ke-size="size16">시간을 재는 곳마다 저런 구조로 코드를 짜야한다면 매우 더러울 것이다.</p> <p data-ke-size="size16">시간을 재는 함수를 짜보자.</p> <pre id="code_1677349668846" class="python" data-ke-language="python" data-ke-type="codeblock"><code>def func(): time.sleep(random.random() * 1) def get_ellapsed_time(func): start = time.time() func() ellapsed_time = time.time() - start print(f"Ellapsed Time: {ellapsed_time:.2f} seconds") get_ellapsed_time(func)</code></pre> <p data-ke-size="size16">func 함수 자체를 get_ellapsed_time 에 넘겨서 위와 같이 짤 수 있다.</p> <p data-ke-size="size16">이정도도 사실 충분하지만 코드가 이쁘지는 않다.</p> <p data-ke-size="size16">시간을 재는 행위가 코드 구조에 영향을 미치는 것이 맘에 들지 않는다.</p> <p data-ke-size="size16">(우리는 불편함을 느껴야한다)</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">시간을 재면서 func 의 작업을 하는 함수를 만들어 보자.</p> <pre id="code_1677350077235" class="python" data-ke-language="python" data-ke-type="codeblock"><code>def func(): time.sleep(random.random() * 1) def get_time_func(func): def time_func(): start = time.time() func() ellapsed_time = time.time() - start print(f"Ellapsed Time: {ellapsed_time:.2f} seconds") return time_func time_func = get_time_func(func) time_func()</code></pre> <p data-ke-size="size16">get_time_func 으로 func 를 감싸면 func 의 작업도 수행하면서 시간을 재는 time_func 를 만들 수 있다.</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">이제 main 코드에서 시간을 재더라도 코드 구조가 바뀌지 않는다.</p> <p data-ke-size="size16">의식의 흐름처럼 왔지만</p> <pre id="code_1677350236715" class="python" data-ke-language="python" data-ke-type="codeblock"><code>time_func = get_time_func(func)</code></pre> <p data-ke-size="size16">해당 코드는</p> <pre id="code_1677350319775" class="python" data-ke-language="python" data-ke-type="codeblock"><code>@get_time_func def func(): time.sleep(random.random() * 1) func()</code></pre> <p data-ke-size="size16">@get_time_func 을 함수 정의 위에 붙여주는 것으로 같은 기능을 할 수 있다.</p> <p data-ke-size="size16">이것이 데코레이터 이다.</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">데코레이터에는 중요한 작업을 맡기지 않는다.</p> <p data-ke-size="size16">예시로 보여준 시간 체크 같이 주요 로직에 직접적 관련이 없는 작업에 주로 사용하는 것이 좋은 것 같다.</p> <p data-ke-size="size16">(@classmethod 의 경우 인자 값 자체가 바뀌긴한다.)</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">장점으로는 메인 소스코드의 로직이 전혀 바뀌지 않는다는 것이다.</p> <h2 data-ke-size="size26">타입 체크 (type check)</h2> <p data-ke-size="size16">파이썬은 타입에 관대하지만 가면 갈수록 타입이 중요하다.</p> <p data-ke-size="size16">타입을 강제화 하는 데코레이터를 만들어보자</p> <pre id="code_1677350707525" class="python" data-ke-language="python" data-ke-type="codeblock"><code>def add_int(a: int, b: int) -&gt; int: return a + b</code></pre> <p data-ke-size="size16">이 함수는 오직 int 만을 위한 함수이다.</p> <p data-ke-size="size16">float 이 들어오기를 바라지 않는다.</p> <p data-ke-size="size16">그렇다면 인자의 type 이 int 가 아닌 경우 오류를 raise 해주는 장치가 필요하다.</p> <pre id="code_1677350869177" class="python" data-ke-language="python" data-ke-type="codeblock"><code>def add_int(a: int, b: int) -&gt; int: if not isinstance(a, int): raise TypeError("a 는 정수여야 합니다.") if not isinstance(b, int): raise TypeError("b 는 정수여야 합니다.") return a + b</code></pre> <p data-ke-size="size16">그런데.. int만 받아야하는 함수가 100개라면 어떨까.</p> <p data-ke-size="size16">타입체크를 위해 추가된 4줄(공백제외)의 소스코드가 400줄이 되어 버릴 것이다.</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">데코레이터를 사용하자.</p> <pre id="code_1677351063860" class="python" data-ke-language="python" data-ke-type="codeblock"><code>def only_int(func): def inner(a, b): if not isinstance(a, int): raise TypeError("a 는 정수여야 합니다.") if not isinstance(b, int): raise TypeError("b 는 정수여야 합니다.") return func(a, b) return inner @only_int def add_int(a: int, b: int) -&gt; int: return a + b print(add_int(1, 2)) # 3 print(add_int(1, 2.)) """ Traceback (most recent call last): File ".\6.py", line 18, in &lt;module&gt; print(add_int(1, 2.)) File ".\6.py", line 8, in inner raise TypeError("b 는 정수여야 합니다.") TypeError: b 는 정수여야 합니다. """</code></pre> <h2 data-ke-size="size26">고급 타입 체크 (advanced type check)</h2> <p data-ke-size="size16">float 만 받고 싶다면 어떻게 해야할까</p> <p data-ke-size="size16">only_float 데코레이터를 만들어서 사용하면 되겠지..?</p> <p data-ke-size="size16">str 만 받고 싶다면?</p> <p data-ke-size="size16">타입도 지정할 수 있으면 좋을 것 같다.</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">다음과 같이 하면 된다.</p> <pre id="code_1677351420509" class="python" data-ke-language="python" data-ke-type="codeblock"><code>def type_check(t: type): def inner1(func): def inner2(*args): for arg in args: if not isinstance(arg, t): raise TypeError(f"argument 는 {t} 타입 이여야 합니다.") return func(*args) return inner2 return inner1 @type_check(int) def add_int(a: int, b: int) -&gt; int: return a + b print(add_int(1, 2.)) """ Traceback (most recent call last): File ".\7.py", line 18, in &lt;module&gt; print(add_int(1, 2.)) File ".\7.py", line 7, in inner2 raise TypeError(f"argument 는 {t} 타입 이여야 합니다.") TypeError: argument 는 &lt;class 'int'&gt; 타입 이여야 합니다. """</code></pre> <p data-ke-size="size16">&nbsp;</p> 지식/Python decorator Python Time Type 데코레이터 문법 시간 체크 타입 파이썬 ZeroAct https://zeroact.tistory.com/36 https://zeroact.tistory.com/36#entry36comment Sun, 26 Feb 2023 03:58:43 +0900 [Ultralytics Hub Flutter Clone Coding] #2. 홈 만들기 https://zeroact.tistory.com/35 <p data-ke-size="size16">저번 포스팅에서 사이드 메뉴를 만들었다.</p> <p data-ke-size="size16">이번에는 홈 화면을 구성해보려고 한다.</p> <p><figure class="imageblock alignCenter" data-ke-mobileStyle="widthOrigin" data-origin-width="1478" data-origin-height="1125"><span data-url="https://blog.kakaocdn.net/dn/nMzru/btrYV9qDjIB/wTkxKg3PcKjs2e5CVK0ED1/img.png" data-phocus="https://blog.kakaocdn.net/dn/nMzru/btrYV9qDjIB/wTkxKg3PcKjs2e5CVK0ED1/img.png"><img src="https://blog.kakaocdn.net/dn/nMzru/btrYV9qDjIB/wTkxKg3PcKjs2e5CVK0ED1/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnMzru%2FbtrYV9qDjIB%2FwTkxKg3PcKjs2e5CVK0ED1%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="1478" height="1125" data-origin-width="1478" data-origin-height="1125"/></span></figure> </p> <p data-ke-size="size16">오른쪽 아이템들은 카드로 구성되어 있다.</p> <p><figure class="imageblock alignCenter" data-ke-mobileStyle="widthOrigin" data-origin-width="842" data-origin-height="348"><span data-url="https://blog.kakaocdn.net/dn/kQrvj/btrYViadoil/WD3O8Zkyk6INzO5V8Yvoyk/img.png" data-phocus="https://blog.kakaocdn.net/dn/kQrvj/btrYViadoil/WD3O8Zkyk6INzO5V8Yvoyk/img.png"><img src="https://blog.kakaocdn.net/dn/kQrvj/btrYViadoil/WD3O8Zkyk6INzO5V8Yvoyk/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkQrvj%2FbtrYViadoil%2FWD3O8Zkyk6INzO5V8Yvoyk%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="842" height="348" data-origin-width="842" data-origin-height="348"/></span></figure> </p> <p data-ke-size="size16">5개의 위젯을 가지고 구성하면 될 것 같다.</p> <p data-ke-size="size16">밑에 빠른 액세스 카드는 이미지가 없기 때문에 이미지는 optional 로 두고</p> <p data-ke-size="size16">인자에 따라 layout 을 다르게 구성하면 될 것 같다.</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">Flutter 에서는 Card 위젯을 제공한다.</p> <p data-ke-size="size16">하지만 Card 위젯에는 사이즈를 지정하는 옵션이 없다.</p> <p data-ke-size="size16">찾아보니 SizedBox 라는 위젯이 있고 이를 사용해 Card를 감싸면</p> <p data-ke-size="size16">SizedBox 내의 사용가능한 공간 만큼 Card 가 expand 되어 표시되게 된다.</p> <p><figure class="imagegridblock"> <div class="image-container"><span data-url="https://blog.kakaocdn.net/dn/l25Lt/btrY1mJKWcz/7D5oHzU61Hd94GBGyjaAlk/img.png" data-phocus="https://blog.kakaocdn.net/dn/l25Lt/btrY1mJKWcz/7D5oHzU61Hd94GBGyjaAlk/img.png" data-origin-width="660" data-origin-height="419" data-is-animation="false" style="width: 49.4186%; margin-right: 10px;" data-widthpercent="50"><img src="https://blog.kakaocdn.net/dn/l25Lt/btrY1mJKWcz/7D5oHzU61Hd94GBGyjaAlk/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl25Lt%2FbtrY1mJKWcz%2F7D5oHzU61Hd94GBGyjaAlk%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="660" height="419"/></span><span data-url="https://blog.kakaocdn.net/dn/8ERwK/btrYSD0PNG4/obTvCmOVPUIu7YCG0xsoRk/img.png" data-phocus="https://blog.kakaocdn.net/dn/8ERwK/btrYSD0PNG4/obTvCmOVPUIu7YCG0xsoRk/img.png" data-origin-width="660" data-origin-height="419" data-is-animation="false" style="width: 49.4186%;" data-widthpercent="50"><img src="https://blog.kakaocdn.net/dn/8ERwK/btrYSD0PNG4/obTvCmOVPUIu7YCG0xsoRk/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8ERwK%2FbtrYSD0PNG4%2FobTvCmOVPUIu7YCG0xsoRk%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="660" height="419"/></span></div> <figcaption>좌) Card, 우) Card in SizedBox</figcaption> </figure> </p> <p data-ke-size="size16">Card 의 Layout 을 생각해보자</p> <p><figure class="imageblock alignCenter" data-ke-mobileStyle="widthOrigin" data-origin-width="876" data-origin-height="423"><span data-url="https://blog.kakaocdn.net/dn/bnut5W/btrY5ljqhNK/yJCqiasCt2vKsQ4yAY2dmk/img.png" data-phocus="https://blog.kakaocdn.net/dn/bnut5W/btrY5ljqhNK/yJCqiasCt2vKsQ4yAY2dmk/img.png"><img src="https://blog.kakaocdn.net/dn/bnut5W/btrY5ljqhNK/yJCqiasCt2vKsQ4yAY2dmk/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbnut5W%2FbtrY5ljqhNK%2FyJCqiasCt2vKsQ4yAY2dmk%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="876" height="423" data-origin-width="876" data-origin-height="423"/></span></figure> </p> <p data-ke-size="size16">최상단 Layout 은 Row 가 될 것이고</p> <p data-ke-size="size16">왼쪽에는 Column 을 사용하면 될 것 같다.</p> <p data-ke-size="size16">Column 에는 아이콘, 제목, 설명, 버튼 위젯을 차곡 차곡 쌓으면 될 것 같다.</p> <p data-ke-size="size16">오른쪽에는 이미지 소스를 준다면 출력하고 아니면 아무것도 생성하지 않도록 조건을 걸면 좋을 것 같다.</p> <pre id="code_1676187929387" data-ke-language="java" data-ke-type="codeblock"><code>class CustomCard extends StatelessWidget { final IconData icon; final String title; final String subTitle; final double width; final double height; final String? buttonText; final Function? buttonHandler; final String? imageSrc; const CustomCard({ required this.icon, required this.title, required this.subTitle, this.width = 400, this.height = 200, this.buttonText, this.buttonHandler, this.imageSrc, super.key, }); }</code></pre> <p data-ke-size="size16">icon, title, subTitle 은 필수 인자로 설정하고 나머지는 optional 및 Nullable 로 지정하였다.&nbsp;</p> <pre id="code_1676188158948" class="java" data-ke-language="java" data-ke-type="codeblock"><code> @override Widget build(BuildContext context) { return SizedBox( height: height, width: width, child: Card( elevation: 20, child: Padding( padding: const EdgeInsets.all(10), child: Row( children: [ Expanded( flex: 10, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon(icon), const Spacer(flex: 1), Text( title, style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 22, ), ), const Spacer(flex: 2), Text( subTitle, style: const TextStyle( fontSize: 18, ), ), if (buttonText != null) ...[ const Spacer(flex: 4), ElevatedButton( onPressed: () =&gt; buttonHandler ?? {}, child: Text(buttonText ?? ""), ) ] ], ), ), if (imageSrc != null) ...[ const Spacer(flex: 1), Image.network( imageSrc ?? "", width: 100, ) ] ], ), ), )); }</code></pre> <p data-ke-size="size16">Flutter 의 layout 구성 방법이 꽤나 직관적이라고 생각했었는데</p> <p data-ke-size="size16">이렇게 놓고 보니 가독성이 좀 구린 것 같다. (제가 부족한 탓일 겁니다.)</p> <p data-ke-size="size16">어쨋든 이렇게 조건에 따라서 다른 뷰 세가지를 만들 수 있다.</p> <p><figure class="imageblock alignCenter" data-ke-mobileStyle="widthOrigin" data-origin-width="660" data-origin-height="418"><span data-url="https://blog.kakaocdn.net/dn/c3LEdK/btrY4mvW9sY/lgpuokyrisVXJC2PlsivM0/img.png" data-phocus="https://blog.kakaocdn.net/dn/c3LEdK/btrY4mvW9sY/lgpuokyrisVXJC2PlsivM0/img.png"><img src="https://blog.kakaocdn.net/dn/c3LEdK/btrY4mvW9sY/lgpuokyrisVXJC2PlsivM0/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc3LEdK%2FbtrY4mvW9sY%2FlgpuokyrisVXJC2PlsivM0%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="660" height="418" data-origin-width="660" data-origin-height="418"/></span></figure> <figure class="imageblock alignCenter" data-ke-mobileStyle="widthOrigin" data-origin-width="660" data-origin-height="418"><span data-url="https://blog.kakaocdn.net/dn/elhKwT/btrY2nIBD3d/wLeKpgmLo7CJ3vjH27tqY0/img.png" data-phocus="https://blog.kakaocdn.net/dn/elhKwT/btrY2nIBD3d/wLeKpgmLo7CJ3vjH27tqY0/img.png"><img src="https://blog.kakaocdn.net/dn/elhKwT/btrY2nIBD3d/wLeKpgmLo7CJ3vjH27tqY0/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FelhKwT%2FbtrY2nIBD3d%2FwLeKpgmLo7CJ3vjH27tqY0%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="660" height="418" data-origin-width="660" data-origin-height="418"/></span></figure> <figure class="imageblock alignCenter" data-ke-mobileStyle="widthOrigin" data-origin-width="660" data-origin-height="418"><span data-url="https://blog.kakaocdn.net/dn/bOnMSv/btrYY59g2BL/aUXfjJZtb5kwjKSglaFb5k/img.png" data-phocus="https://blog.kakaocdn.net/dn/bOnMSv/btrYY59g2BL/aUXfjJZtb5kwjKSglaFb5k/img.png"><img src="https://blog.kakaocdn.net/dn/bOnMSv/btrYY59g2BL/aUXfjJZtb5kwjKSglaFb5k/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOnMSv%2FbtrYY59g2BL%2FaUXfjJZtb5kwjKSglaFb5k%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="660" height="418" data-origin-width="660" data-origin-height="418"/></span></figure> </p> <p data-ke-size="size16">하지만 텍스트가 길어지면 layout을 넘어가서 오류가 뜬다.</p> <p><figure class="imageblock alignCenter" data-ke-mobileStyle="widthOrigin" data-origin-width="660" data-origin-height="418"><span data-url="https://blog.kakaocdn.net/dn/oIuaN/btrYYqr8HAF/HH2GPlOsYKhNk8OKb1wZa1/img.png" data-phocus="https://blog.kakaocdn.net/dn/oIuaN/btrYYqr8HAF/HH2GPlOsYKhNk8OKb1wZa1/img.png"><img src="https://blog.kakaocdn.net/dn/oIuaN/btrYYqr8HAF/HH2GPlOsYKhNk8OKb1wZa1/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoIuaN%2FbtrYYqr8HAF%2FHH2GPlOsYKhNk8OKb1wZa1%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="660" height="418" data-origin-width="660" data-origin-height="418"/></span></figure> </p> <p data-ke-size="size16">이를 해결하기 위해 누군가 이쁜 위젯을 만들어 공유해주셨다.</p> <p><figure class="imageblock alignCenter" data-ke-mobileStyle="widthOrigin" data-origin-width="896" data-origin-height="334"><span data-url="https://blog.kakaocdn.net/dn/dorcbs/btrY1mCZl4K/V9peBdFZceLEj6FgcABnmK/img.png" data-phocus="https://blog.kakaocdn.net/dn/dorcbs/btrY1mCZl4K/V9peBdFZceLEj6FgcABnmK/img.png"><img src="https://blog.kakaocdn.net/dn/dorcbs/btrY1mCZl4K/V9peBdFZceLEj6FgcABnmK/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdorcbs%2FbtrY1mCZl4K%2FV9peBdFZceLEj6FgcABnmK%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="764" height="285" data-origin-width="896" data-origin-height="334"/></span></figure> <figure class="imageblock alignCenter" data-ke-mobileStyle="widthOrigin" data-origin-width="660" data-origin-height="418"><span data-url="https://blog.kakaocdn.net/dn/bkONvV/btrYSCt2527/s6OgRJBypimRLtnCjhbVk0/img.png" data-phocus="https://blog.kakaocdn.net/dn/bkONvV/btrYSCt2527/s6OgRJBypimRLtnCjhbVk0/img.png"><img src="https://blog.kakaocdn.net/dn/bkONvV/btrYSCt2527/s6OgRJBypimRLtnCjhbVk0/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkONvV%2FbtrYSCt2527%2Fs6OgRJBypimRLtnCjhbVk0%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="660" height="418" data-origin-width="660" data-origin-height="418"/></span></figure> </p> <p data-ke-size="size16">해당 위젯을 사용하니 가용 영역에 맞게 폰트 크기가 자동으로 조절되어 벗어나지 않게 되었다.</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">작성한 CustomCard 클래스를 사용해 홈을 구성했다.</p> <p><figure class="imageblock alignCenter" data-ke-mobileStyle="widthOrigin" data-origin-width="1303" data-origin-height="723"><span data-url="https://blog.kakaocdn.net/dn/U8maq/btrYUuB0baE/0GgakKNiDA34KI5P0qRwd1/img.png" data-phocus="https://blog.kakaocdn.net/dn/U8maq/btrYUuB0baE/0GgakKNiDA34KI5P0qRwd1/img.png"><img src="https://blog.kakaocdn.net/dn/U8maq/btrYUuB0baE/0GgakKNiDA34KI5P0qRwd1/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FU8maq%2FbtrYUuB0baE%2F0GgakKNiDA34KI5P0qRwd1%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="1303" height="723" data-origin-width="1303" data-origin-height="723"/></span></figure> </p> <p data-ke-size="size16">&nbsp;</p> clone coding DART Flutter ultralytics YOLO 클론코딩 ZeroAct https://zeroact.tistory.com/35 https://zeroact.tistory.com/35#entry35comment Sun, 12 Feb 2023 17:21:03 +0900 [Ultralytics Hub Flutter Clone Coding] #1. 사이드 메뉴 만들기 https://zeroact.tistory.com/34 <p data-ke-size="size16">강의가 지루해서 클론 코딩을 시작해보려고 한다.</p> <p data-ke-size="size16">클론 코딩의 대상은 Yolo 를 만든 Ultralytics 라는 회사의 딥러닝 플랫폼 제품이다.</p> <p data-ke-size="size16"><a href="https://hub.ultralytics.com/" target="_blank" rel="noopener">https://hub.ultralytics.com/</a></p> <figure id="og_1676098121739" contenteditable="false" data-ke-type="opengraph" data-ke-align="alignCenter" data-og-type="website" data-og-title="Ultralytics HUB" data-og-description="" data-og-host="hub.ultralytics.com" data-og-source-url="https://hub.ultralytics.com/" data-og-url="https://hub.ultralytics.com/" data-og-image=""><a href="https://hub.ultralytics.com/" target="_blank" rel="noopener" data-source-url="https://hub.ultralytics.com/"> <div class="og-image" style="background-image: url();">&nbsp;</div> <div class="og-text"> <p class="og-title" data-ke-size="size16">Ultralytics HUB</p> <p class="og-desc" data-ke-size="size16">&nbsp;</p> <p class="og-host" data-ke-size="size16">hub.ultralytics.com</p> </div> </a></figure> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">프론트엔드 코딩에 대한 지식이 전무하지만 어떻게든 되겠지 하는 마음으로 해보려한다 ㅎㅎㅎ</p> <h2 data-ke-size="size26">홈 화면</h2> <p><figure class="imageblock alignCenter" data-ke-mobileStyle="widthOrigin" data-origin-width="1478" data-origin-height="1125"><span data-url="https://blog.kakaocdn.net/dn/c624ao/btrYRU9kAmf/2kSbJF6VklzYqxBcnY3NIk/img.png" data-phocus="https://blog.kakaocdn.net/dn/c624ao/btrYRU9kAmf/2kSbJF6VklzYqxBcnY3NIk/img.png"><img src="https://blog.kakaocdn.net/dn/c624ao/btrYRU9kAmf/2kSbJF6VklzYqxBcnY3NIk/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc624ao%2FbtrYRU9kAmf%2F2kSbJF6VklzYqxBcnY3NIk%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="1478" height="1125" data-origin-width="1478" data-origin-height="1125"/></span></figure> </p> <p data-ke-size="size16">왼쪽에는 접어지는 메뉴가 있고</p> <p data-ke-size="size16">홈, 데이터 셋, 프로젝트, 모델, 기타로 구성되어 있다.</p> <p data-ke-size="size16">클론 코딩 대상은</p> <p data-ke-size="size16">홈 ,데이터 셋, 모델 정도로 하려고 한다.</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">오른쪽에는 튜토리얼과 빠른 액세스, 계정 정보가 보여진다.</p> <h4 data-ke-size="size20">사이드 메뉴</h4> <p data-ke-size="size16">flutter 위젯 중에 비슷한게 생긴 것을 찾아 보니 side_navigation 이라는 것이 있었다.</p> <p><figure class="imageblock alignCenter" data-ke-mobileStyle="widthOrigin" data-origin-width="1004" data-origin-height="185"><span data-url="https://blog.kakaocdn.net/dn/djO0xR/btrYY6Np2Up/EQRfkCU4RowwQ2G1Ans92K/img.png" data-phocus="https://blog.kakaocdn.net/dn/djO0xR/btrYY6Np2Up/EQRfkCU4RowwQ2G1Ans92K/img.png"><img src="https://blog.kakaocdn.net/dn/djO0xR/btrYY6Np2Up/EQRfkCU4RowwQ2G1Ans92K/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdjO0xR%2FbtrYY6Np2Up%2FEQRfkCU4RowwQ2G1Ans92K%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="1004" height="185" data-origin-width="1004" data-origin-height="185"/></span></figure> </p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">pubspec.yaml 파일에 다음과 같이 추가하면 알아서 의존성이 설치된다.</p> <p><figure class="imageblock alignCenter" data-ke-mobileStyle="widthOrigin" data-origin-width="946" data-origin-height="436"><span data-url="https://blog.kakaocdn.net/dn/bWnXlG/btrYRQ64UFq/3lvmzwnDD63aacCGvSHsx1/img.png" data-phocus="https://blog.kakaocdn.net/dn/bWnXlG/btrYRQ64UFq/3lvmzwnDD63aacCGvSHsx1/img.png"><img src="https://blog.kakaocdn.net/dn/bWnXlG/btrYRQ64UFq/3lvmzwnDD63aacCGvSHsx1/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWnXlG%2FbtrYRQ64UFq%2F3lvmzwnDD63aacCGvSHsx1%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="946" height="436" data-origin-width="946" data-origin-height="436"/></span></figure> </p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">SideNavigation 의 Header 는 Ultralytics의 로고를 가져와서 색만 바꿔주었다.</p> <p data-ke-size="size16">SideNavigation 의 항목은 SideNavigationBarItem 을 list 로 넣어주면 간단하게 항목을 구성할 수 있다.</p> <p data-ke-size="size16">해당 항목이 선택 되었을 때 표시할 오른쪽 내용물도 함께 Map 자료구조를 통해 함께 넣어 두었다.</p> <pre id="code_1676114126496" class="java" data-ke-language="java" data-ke-type="codeblock"><code>SideNavigationBarHeader header = SideNavigationBarHeader( image: Image.network( "https://github.com/ultralytics/assets/blob/main/logo/Ultralytics-logomark-color.png?raw=true", width: 50, color: const Color(0xFFaaaa00), ), title: const Text("Flultralytics Hub"), subtitle: const Text("Huijae")); List&lt;Map&gt; menuItems = const [ { 'navItem': SideNavigationBarItem( icon: Icons.home, label: 'Home', ), 'view': Text('Home'), }, { 'navItem': SideNavigationBarItem( icon: Icons.data_array, label: 'Datasets', ), 'view': Text('Datasets'), }, { 'navItem': SideNavigationBarItem( icon: Icons.broken_image_rounded, label: 'Models', ), 'view': Text('Models'), }, ];</code></pre> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">Navigation 은 보통 왼쪽에 있고 내용물이 오른쪽에 있으므로 Row 를 통해 layout 을 구성해준다.</p> <p data-ke-size="size16">전체 화면의 구성물들은 인덱스가 바뀔때마다 Rendering 되어야 하므로 StatefulWidget 으로 만든다.</p> <pre id="code_1676114287395" class="java" data-ke-language="java" data-ke-type="codeblock"><code>class MainView extends StatefulWidget { const MainView({Key? key}) : super(key: key); @override MainViewState createState() =&gt; MainViewState(); } class MainViewState extends State&lt;MainView&gt; { int selectedIndex = 0; @override Widget build(BuildContext context) { return Scaffold( body: Row( children: [ SideNavigationBar( header: header, selectedIndex: selectedIndex, items: menuItems .map&lt;SideNavigationBarItem&gt;((item) =&gt; item['navItem']) .toList(), onTap: (index) =&gt; setState(() { selectedIndex = index; }), ), Expanded( child: menuItems.elementAt(selectedIndex)['view'], ) ], ), ); } }</code></pre> <p data-ke-size="size16">여기서 항목들 리스트들을 받아오는데 type cast 때문에 애를 먹었다.</p> <p data-ke-size="size16">map 함수를 통해 list 를 만들경우 내용물의 type 을 장담할 수 없기 때문인데,</p> <p data-ke-size="size16">map&lt;타입&gt; 문법을 사용하면 map 의 결과물들이 지정 type 으로 casting 된다.</p> <p data-ke-size="size16">이게 권장되는 문법인지는 잘 모르겠다.</p> <h2 data-ke-size="size26">결과물</h2> <p><figure class="imagegridblock"> <div class="image-container"><span data-url="https://blog.kakaocdn.net/dn/culbxQ/btrYRSqjVAC/aG6vidsGnueRQeMS5Cs7o1/img.png" data-phocus="https://blog.kakaocdn.net/dn/culbxQ/btrYRSqjVAC/aG6vidsGnueRQeMS5Cs7o1/img.png" data-origin-width="1509" data-origin-height="904" data-is-animation="false" style="width: 49.4186%; margin-right: 10px;" data-widthpercent="50"><img src="https://blog.kakaocdn.net/dn/culbxQ/btrYRSqjVAC/aG6vidsGnueRQeMS5Cs7o1/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FculbxQ%2FbtrYRSqjVAC%2FaG6vidsGnueRQeMS5Cs7o1%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="1509" height="904"/></span><span data-url="https://blog.kakaocdn.net/dn/dfNihc/btrYTV0zo4Z/XfvOfi5qaYqUf4jjC7KTR1/img.png" data-phocus="https://blog.kakaocdn.net/dn/dfNihc/btrYTV0zo4Z/XfvOfi5qaYqUf4jjC7KTR1/img.png" data-origin-width="1509" data-origin-height="904" data-is-animation="false" style="width: 49.4186%;" data-widthpercent="50"><img src="https://blog.kakaocdn.net/dn/dfNihc/btrYTV0zo4Z/XfvOfi5qaYqUf4jjC7KTR1/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdfNihc%2FbtrYTV0zo4Z%2FXfvOfi5qaYqUf4jjC7KTR1%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="1509" height="904"/></span></div> </figure> </p> <p data-ke-size="size16">좌측 메뉴를 클릭하면 우측 내용물이 잘 바뀌고,</p> <p data-ke-size="size16">메뉴를 최소화하여 아이콘만 보이게 할 수도 있다.</p> <p><figure class="imageblock alignCenter" data-ke-mobileStyle="widthOrigin" data-origin-width="1509" data-origin-height="904"><span data-url="https://blog.kakaocdn.net/dn/kGDde/btrYV8LGOOs/EqlvAdLj3yTTHtEyFhEBW1/img.png" data-phocus="https://blog.kakaocdn.net/dn/kGDde/btrYV8LGOOs/EqlvAdLj3yTTHtEyFhEBW1/img.png"><img src="https://blog.kakaocdn.net/dn/kGDde/btrYV8LGOOs/EqlvAdLj3yTTHtEyFhEBW1/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkGDde%2FbtrYV8LGOOs%2FEqlvAdLj3yTTHtEyFhEBW1%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="1509" height="904" data-origin-width="1509" data-origin-height="904"/></span></figure> </p> <p data-ke-size="size16">색이 못 생겨서 바꿔보았는데 역시 미적인 것은 안 건드리는 것이 나은 것 같다.</p> 지식/Flutter clone coding DART Flutter ultralytics YOLO 클론코딩 ZeroAct https://zeroact.tistory.com/34 https://zeroact.tistory.com/34#entry34comment Sat, 11 Feb 2023 20:37:08 +0900 사람을 미워하지 않는 방법 https://zeroact.tistory.com/33 <p data-ke-size="size16">사람에 대한 생각을 많이 한다.</p> <p data-ke-size="size16">정말 많이 한다.</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">책을 많이 읽지는 않지만,</p> <p data-ke-size="size16">일상생활이나 영화, 드라마 심지어 게임을 하면서도</p> <p data-ke-size="size16">그 자체를 즐기는 것 보다는 사람에 대한 생각을 많이한다.</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">"너는 특별한 존재야" 라는 말은&nbsp;</p> <p data-ke-size="size16">그 말 자체로</p> <p data-ke-size="size16">나를 특별하게 만들어 준다.</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">너도 특별하고</p> <p data-ke-size="size16">나도 특별하고</p> <p data-ke-size="size16">저쪽 어디 사는 사람도 특별하고</p> <p data-ke-size="size16">이제 막 태어난 아기도 특별하고</p> <p data-ke-size="size16">곧 죽음을 맞이하는 사람도 특별하다.</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">모든 사람은 특별하다.</p> <p data-ke-size="size16">그렇기 때문에 특별한건 특별하지 않다.</p> <p data-ke-size="size16">조금 슬픈 것 같기도하다.</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">그런데 역설적이게도</p> <p data-ke-size="size16">나는 특별하지 않고</p> <p data-ke-size="size16">너도 특별하지 않다는 사실을 깨닫고 받아들이면</p> <p data-ke-size="size16">&nbsp;</p> <p data-ke-size="size16">사람을 미워하지 않게된다.</p> ZeroAct https://zeroact.tistory.com/33 https://zeroact.tistory.com/33#entry33comment Sat, 4 Feb 2023 23:15:26 +0900