<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>castle_mi.log</title>
        <link>https://velog.io/</link>
        <description>데이터 분석가(가 되고픈) 황성미입니다!</description>
        <lastBuildDate>Mon, 12 Feb 2024 13:12:15 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>castle_mi.log</title>
            <url>https://velog.velcdn.com/images/castle_mi/profile/8caa1fe8-c3da-46ea-b109-83c5f57a9770/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. castle_mi.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/castle_mi" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[통계/IT공학 졸업생이 제로베이스를 수강한 이유_제로베이스 데이터 취업 스쿨 수강 후기]]></title>
            <link>https://velog.io/@castle_mi/%ED%86%B5%EA%B3%84IT%EA%B3%B5%ED%95%99-%EC%A1%B8%EC%97%85%EC%83%9D%EC%9D%B4-%EC%A0%9C%EB%A1%9C%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%A5%BC-%EC%88%98%EA%B0%95%ED%95%9C-%EC%9D%B4%EC%9C%A0%EC%A0%9C%EB%A1%9C%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%B7%A8%EC%97%85-%EC%8A%A4%EC%BF%A8-%EC%88%98%EA%B0%95-%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@castle_mi/%ED%86%B5%EA%B3%84IT%EA%B3%B5%ED%95%99-%EC%A1%B8%EC%97%85%EC%83%9D%EC%9D%B4-%EC%A0%9C%EB%A1%9C%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%A5%BC-%EC%88%98%EA%B0%95%ED%95%9C-%EC%9D%B4%EC%9C%A0%EC%A0%9C%EB%A1%9C%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%B7%A8%EC%97%85-%EC%8A%A4%EC%BF%A8-%EC%88%98%EA%B0%95-%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Mon, 12 Feb 2024 13:12:15 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요! 오늘은 6-7개월간 수강했던 제로베이스에 대한 이야기를 해보려고 합니다🥰</p>
<br>

<h1 id="제로베이스를-수강하기-전-나는">제로베이스를 수강하기 전 나는..</h1>
<p>저는 통계학과와 IT 공학과를 복수전공으로 졸업하고 23년 2월 사회로 첫 발을 디디게 되었습니다..!!ㅋㅋㅋ 졸업 후 몇 달간은 ‘내가 어떤 일을 해야할까’에 대해 많이 고민했었어요.</p>
<p>통계와 IT공학을 전공하면서 자연스럽게 데이터분석가/데이터 사이언티스트의 직무를 생각해보기도 했지만, 어떤 산업군으로 진출해야 할지, 제 어설픈 완벽주의 성격 때문에 이 직종을 선택하면 집이나 회사에서 컴퓨터 앞에 앉아있어 고생만 하게 되는 것이 아닐지 등등 여러 가지 고민이 있었어요.</p>
<p>하지만 데이터 직무에 대한 열정과 배움에 대한 열망으로 인해 데이터 직무에 도전하기로 마음먹었고, 대학 시절 배웠던 내용을 정리하기 시작했습니다. 채용 공고를 보면서 필요한 역량을 파악하고, 배우고 싶었던 Tableau 강의를 수강하며, 다양한 회사의 기술 블로그를 찾아보며 실제 기업들의 업무 현황을 익히고 카드 뉴스를 만들어 공스타그램에 업로드하며 일상을 이어나갔습니다.</p>
<p>그럼에도 불구하고 몇 가지 어려움이 있었는데요… </p>
<p>⛰️ 1. 어떤 산업군에 지원해야 할지 모르겠음</p>
<p>⛰️ 2. 지원하려는 기업에 적합한 프로젝트를 수행했는지 모르겠음</p>
<p>⛰️ 3. 이력서와 포트폴리오는 어떻게 작성해야 할지 모르겠음</p>
<p>음.. 이런 어려움들이 있었는데요… 생각해보면 더 많은 고민이 있었겠지만, 이런 이유들로 인해 기업에 지원할 준비가 제대로 되지 않았다고 판단했어요.</p>
<p>그렇게 물음표만 가득한 상황에서 취업 준비를 하다보니 점점 자존감도 낮아지는 것 같았어요. 뭐라도 해보자는 생각을 가지고 있던 중 &lt;제로베이스&gt;를 알게 되었습니다.</p>
<br>

<hr>
<h1 id="왜-제로베이스였을까">왜 제로베이스였을까!</h1>
<p>국민 내일 배움 카드도 발급 받고, 국비 교육도 알아보고 다방면으로 알아보던 제가 제로베이스를 시작하게 된 건 여러 이유가 있었습니다.</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/88609d43-e4f8-42e2-b66a-9c69936d02d3/image.png" alt=""></p>
<ol>
<li><p><strong>공개된 경력</strong> 
→ 교수님/강사님들의 경력을 알 수 있어서 믿을 수 있었어요 👍🏻
<img src="https://velog.velcdn.com/images/castle_mi/post/d72948d2-5cd1-428a-b8b2-3473000410c5/image.png" alt=""></p>
</li>
<li><p><strong>탄탄한 커리큘럼</strong> 
→ 국비교육은 커리큘럼을 제공 안 하는 경우도 있었고, 대부분 Python 기초를 몇 달간 수강하는 경우였습니다. 무엇보다 제로베이스에서는 제가 배우고 싶던 <strong>Tableau/SQL</strong>가 포함되어 있어서 좋았습니다😊 
 <img src="https://velog.velcdn.com/images/castle_mi/post/217a0669-a669-4d4d-9685-6b4e6cf9e42b/image.png" alt=""></p>
</li>
</ol>
<ol start="3">
<li><strong>100% 취업연계</strong> 
→ 학생들의 취업 지원에 자신있어하고 졸업생이 100% 취업할 때까지 멘토링을 해준다는 점이 믿음직스러웠습니다 !! 🤓 </li>
</ol>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/f5c9107a-070c-4a16-8eee-fe6b8a5e426f/image.png" alt=""></th>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/8a86b0f6-e835-418a-a316-e6d33a329fbd/image.png" alt=""></th>
</tr>
</thead>
</table>
<ol start="4">
<li><strong>다양한 과제 및 프로젝트</strong> 
→ 출처가 명확한(서울 공공 데이터, 캐글 데이터 등) 다방면 데이터의 EDA와 Python, SQL, Tableau 과제를 할 수 있고 프로젝트를 적어도 3개는 할 수 있다는 커리큘럼이 저를 성장시켜줄 것 같았습니다. 🌟🌟 </li>
</ol>
<p>위 이유들 때문에 다른 교육들보다 제로베이스가 더 믿음직스러웠고 수강하기로 마음 먹었죠!</p>
<br>

<hr>
<h1 id="제로베이스에는-과제가-많아서-좋았다">제로베이스에는 과제가 많아서 좋았다</h1>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/7482bb34-ea46-4e6e-87bb-93aec11b4eaa/image.png" alt=""></p>
<p>그렇게 6월부터 수강한 제로베이스에서 진행한 과제 및 프로젝트들입니다!! 나열 해놓으니깐 많네요 ㅎㅎ</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/2ef018ee-64ff-4adf-b956-a26598f20c67/image.png" alt=""></p>
<p>약 3개월의 교육 기간 동안 다양한 출처의 데이터를 활용하여 원하는 목적에 맞게 데이터프레임을 추출하는 연습을 많이 했습니다.</p>
<p>직접 크롤링하여 데이터를 추출하고, 추출한 데이터를 AWS RDS에 저장한 후 분석 작업을 하고 Tableau를 사용하여 대시보드를 만들기도 했습니다.</p>
<p>이런 과정들 중에서도 가장 많은 <strong>과제를 수행한 경험이</strong> 저를 성장시켜주었고, 특히 마음에 들었습니다.😊😁</p>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/06c93400-283f-4c47-80c3-d6dc0bc5674b/image.png" alt=""></th>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/6f7058ac-3865-4b46-8bfb-a54c0c287648/image.png" alt=""></th>
</tr>
</thead>
</table>
<p>저는 제로베이스 수강 전에 Tableau 온라인 강의를 결제해두고 작심삼일로 끝냈던 적이 있었는데, 그래서 제로베이스의 Tableau 과제는 기술을 익히고 직접 대시보드를 만들 수 있는 좋은 기회였습니다.</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/84189655-b726-466c-9398-2cf3704692a4/image.png" alt=""></p>
<p>또한, 채용공고를 살펴보면 ‘서버에 연결된 데이터를 분석한 경험’ 과 같은 역량이 요구되는 경우가 많았습니다. 하지만 저는 항상 데이터를 다운로드하여 작업해왔기 때문에 🥲 서버에 데이터를 적재하고 프로젝트를 진행하는 경험이 부족했습니다. 그래서 <strong>AWS RDS</strong> 강의는 이런 부분에서 도움이 되었습니다 :) </p>
<p>파이널 프로젝트 때는 데이터 파일이 8개였는데, AWS RDS에 데이터를 적재한 후 SQL을 사용하여 원하는 목적에 따라 데이터를 추출하고 EDA를 진행했습니다. 그 덕분에 데이터 스키마를 쉽게 설계할 수 있었습니다. 이 경험을 통해 다른 서버에 대한 지식도 배우고 싶어졌습니다.💪💪 </p>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/25b69323-65b3-4f3d-a0a6-925d5d09c153/image.png" alt=""> 7_인터파크 영화 예매율 순위 中</th>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/0a2380cb-b6b4-4e4a-a1e9-36a8c2b40fbb/image.png" alt=""> 7_인터파크 영화 예매율 순위 中</th>
</tr>
</thead>
</table>
<p>마지막으로 EDA 테스트 과제도 저를 많이 성장시켜주었습니다. 처음에는 단순히 정답을 맞추는 것에만 집중했었는데, 10개의 과제를 수행하면서 데이터프레임을 위해 필요한 결측치 처리, 이상 데이터 처리, 데이터 변환 등 다양한 작업을 경험하였습니다. 이를 통해 보다 깔끔한 코드로 원하는 데이터프레임을 추출하거나 목표 데이터프레임을 만들기 위한 작업을 경험할 수 있었습니다. 이러한 경험은 이후 프로젝트에서 원하는 목적에 맞게 데이터마트를 생성하는데 큰 도움이 되었습니다.</p>
<br>

<hr>
<h1 id="멘토님을-통해-알게된-데이터마트">멘토님을 통해 알게된 &#39;데이터마트&#39;</h1>
<p>여기서 ‘데이터마트’가 생소하실 수도 있겠죠?</p>
<p>저도 처음에는 이 용어를 알지 못했는데, 프로젝트 중간 중간 이력서/포트폴리오 특강을 들으면서 멘토님을 통해 알게되었습니다.</p>
<p>데이터 분석가나 데이터 사이언티스트가 분석을 위해 먼저 ‘데이터마트’ 를 구축한다고 합니다. 데이터마트는 간단히 말해서 데이터프레임으로, 분석하고자 하는 목적에 맞게 데이터프레임을 추출하는 과정을 의미한다고 배웠습니다. </p>
<p>제가 프로젝트를 진행하면서 처음에는추천 프로젝트를 위한 데이터를 사용했지만, 나중에는 그 데이터를 이용하여 고객 이탈 위험도를 예측해보고자 데이터를 변형하기도 했고, 실제 기업의 기준을 모르는 상황에서 팀원들과 함께   정의하는 과정이 어려웠습니다. 그러나 멘토님께서 그런 과정들이 실제 기업에서 이루어진다는 점을 강조해주셨고, “프로젝트를 단순히 진행하는 것이 아니라 그 과정을 녹여내어 포트폴리오에 보여주세요”라는 조언을 해주셨습니다.  </p>
<p>멘토님의 조언 덕분에 제가 진행한 프로젝트 과정이 단순히 힘들기만 한 것이 아니라 성장의 기회로 이어진다는 것을 깨달았습니다👍🏻</p>
<br>

<hr>
<h1 id="가장-뿌듯했던-파이널-프로젝트">가장 뿌듯했던 파이널 프로젝트</h1>
<p>저는 교육 기간이 지난 후 실제로 프로젝트도 3개를 진행했었는데요. 강의에서 배운 내용을 프로젝트에 어떻게 활용했는지 정리해보려고 합니다. </p>
<p>저는 아래와 같은 프로젝트를 진행했었는데요.</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/2a9e5bf9-c149-4e0d-a27e-009651808e3c/image.png" alt=""></p>
<p>위 프로젝트 중 교수님께 칭찬들었던! 파이널 프로젝트를 잠깐 소개해드리고 싶네요ㅎㅎ</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/64c2b702-3451-4b41-b732-c1f4601ceb06/image.png" alt=""></p>
<p>파이널 프로젝트로 진행한 주제는 &lt;국내 이커머스 기업의 해외 진출 방향 제안&gt;이었습니다. 이 프로젝트는 엔데믹 현상으로 인해 국내 기업들이 해외로 진출하는 추세를 반영하여 진행되었습니다. 그리고 저희가 데이터 팀으로서, &quot;해외 진출을 고려해야 한다&quot;는 업무를 받았다고 가정하고 프로젝트를 진행하였습니다.</p>
<p>이커머스 기업의 데이터를 구하는 것은 어려운 일이기 떄문에, 캐글의 “Olist 데이터”를 활용하여 브라질로의 진출을 고려한다는 가설을 세워보았어요!😆😆</p>
<p>이 데이터는 많은 사람들이 분석해보았고, 선배 기수분들도 많이 다뤄본 데이터였지만, 아무리 8개의 테이블과 10만건이 넘는 데이터라 하더라도 다양한 주제로 파생되기 어려울 것이라는 우려를 교수님께서 조언해주셨습니다. 이러한 상황이 저희 팀원들에게도 도전이었습니다. </p>
<p>그래서 저희는 다양한 주제로 뻗어나가는 것보다는 우리가 배운 내용을 모두 쏟아내보기로 결정했습니다.</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/bba75410-d662-48bc-ad36-fe23ddcfd5c4/image.png" alt=""></p>
<p>먼저 저는 브라질에 대한 이해를 위해 지인을 통해 브라질 분에게 질문하여 브라질 화폐에 대한 지식을 얻었고, Olist 홈페이지에도 문의를 해서 브라질을 이해하기 위해 노력했습니다. 그리고 AWS RDS에 테이블을 적재하여 데이터의 구조를 파악하고 데이터 스키마를 설계하였으며 다양한 데이터를 추출하며 EDA를 진행했습니다.</p>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/69b1f495-3a7d-465d-ba6a-8b26635bbb97/image.png" alt=""></th>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/6e1b867e-dbb2-49c1-b90a-ad1feac52acf/image.png" alt=""></th>
</tr>
</thead>
</table>
<p>EDA 를 통해 얻은 인사이트를 바탕으로 이상 데이터를 처리하고, 저희가 원하는 목적에 맞는 데이터를 추출할 수 있었습니다. 진행했던 EDA 결과를 단순히 나열하기보단, 회사의 동료팀에게 도움이 될만한 내용들을 정리하여 Tableau를 통해 대시보드로 시각화하였습니다.</p>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/d6c1a76f-0e69-4849-93a1-1202e34b6d1e/image.png" alt=""></th>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/6178ca7a-6ab5-476c-bb21-0a7dfef24e91/image.png" alt=""></th>
</tr>
</thead>
</table>
<p>그리고 해외 진출 시 필요할 물류 센터 위치를 Kmeans 알고리즘을 이용하여 후보군을 선정하고, 배송 시간과 배송비를 예측하는 모델을 학습시켜 얼마나 절약될 수 있는지 확인했습니다. 예측 모델링 결과, 후보군의 물류 센터에서 각 지역에 배송을 한다면 약 12%~13%정도 배송 시간과 배송비가 감소할 수 있음을 알 수 있었습니다.</p>
<p>마지막으로, 발표 시 브라질의 상황을 생생하게 그려내어 교수님의 “지금까지 Olist를 발표한 팀 중 가장 잘 발표했다”는 피드백을 받았고, 개인 블로그와 유튜브에도 업로드 되어 많은 사람들에게 공유되었습니다. 😊😊</p>
<p>👇🏻 교수님의 개인 블로그</p>
<p><a href="https://pinkwink.kr/1454">국내 이커머스 기업의 해외 진출 방향 제안이라는 주제로 프로젝트를 수행한 팀을 소개합니다.</a></p>
<p>이 모든 것을 통해 자랑스러운 성과를 이뤄내었다고 뿌듯해 했었지요 ㅎㅎ</p>
<br>

<hr>
<h1 id="온라인으로-팀원과-소통하기">온라인으로 팀원과 소통하기</h1>
<p>모든 것이 온라인으로 이루어지는 &lt;제로베이스&gt;에서 처음 본 팀원들과 프로젝트를 진행하는 것이 항상 순탄하지는 않았습니다.</p>
<p>3개월 동안 온라인으로 팀원들과 함께 뿌듯한 결과물을 만들었습니다. 이를 가능하게 한 이유들을 생각해보았을 때, 아래와 같은 요소들이 큰 역할을 한 것 같습니다.</p>
<ol>
<li><p><strong>온라인 회의는 일주일에 최소 2~3회, 3시간 이상 진행
1-1. 회의를 시작하기 전, 회의의 목표 설정하기. 그리고 아웃풋을 무조건 내기</strong>
: 첫 프로젝트 때는 모두가 어떻게 진행해야 할지 막막했기 때문에 밤을 새거나, 일주일에 5번 이상 만난 적도 있었습니다. 그러나 프로젝트를 진행하면서 어떻게 회의를 진행해야 하고 마무리 해야 하는지 알게 되면서 각 회의의 아웃풋을 수행할 충분한 시간을 가지고 다시 팀원들과 만나 이야기하는 형식으로 진행하니 수월해졌었습니다.</p>
</li>
<li><p><strong>회의에 참석하면 말 많이 하기</strong></p>
<p> <strong>2-1. 자신의 의견을 잘 정리해서 말하기(말이 꼬인다면 글로 적어서 팀원이 이해할 수 있게 전달하기)</strong>
 : 오랜 시간 동안 코드를 보고 일을 하다 보면, 내가 무슨 생각을 하고 있는지나 내가 생각한 의견을 말로 어떻게 전달해야 할지 막막한 순간이 있었습니다. 그럴 땐 글로 의견을 적어보는 것도 매우 유용했습니다.✍</p>
</li>
<li><p><strong>슬랙이나 노션, 피그마 같은 협업 툴 사용하기
3-1. 자신의 진행 상황을 팀원에게 무조건 전달하기</strong>
: 내가 맡은 일의 진행 상황은 반드시 팀원이 알아야 해요! 진행한 방향이 틀렸다면 팀원이 바로 잡아줄 수도 있고, 막히는 부분이 있을 때 팀원들의 의견 덕분에 문제를 해결할 수 있었습니다. 그리고 진행 과정을 알아야 이후 계획을 세울 수 있으니까요 ㅎㅎ    </p>
</li>
</ol>
<p>그리고 협업 툴을 사용하면 굳이 온라인 회의가 아니더라도 진행 과정을 공유할 수 있고 참고 자료나 발표 자료 등 자유롭게 확인할 수 있어서 좋습니다.  </p>
<br>

<hr>
<h1 id="제로베이스가-좋았던-또-다른-이유">제로베이스가 좋았던 또 다른 이유</h1>
<p>마지막으로 제로베이스에서는 공부하다 막히면 언제든지 물어볼 수 있는 기수별 슬랙 채널이 있어서 제가 질문을 엄청 했었습니다.. ㅎㅎ 퀴즈나 과제를 풀다가 궁금했던 점들을 업로드하면 동기 수강생 또는 강사님께서 친절히 답변해주셨어요 !</p>
<p>그리고 온라인 부트캠프이다 보니 내가 원하는 시간에, 내가 원하는 장소에서 마음껏 들을 수 있다는 점도 좋았습니다. IT 강의들은 한 번에 이해하기 힘든 경우가 많기 때문에 여러 번 듣고 이해하는 과정이 필요하잖아요! 그런 점에서 저는 온라인 부트캠프가 좋았었습니다.</p>
<p>이렇게 6-7개월 간의 제로베이스 활동을 정리해보니 정말 많은 어려움을 극복하며 성장했어요. 이 경험을 통해 많은 것을 배우고, 저의 실력도 크게 향상되었습니다.</p>
<p>이제는 이러한 성장한 실력을 이력서와 포트폴리오에 잘 녹여내어, 제 꿈과 목표를 이루기 위해 최선을 다하고자 합니다. </p>
<p>모든 일들이 제 성장을 위한 기회로 다가올 때 열심히 앞으로 한 발자국 내딛어 볼게요!! 화이팅 💪🏻💪🏻</p>
<br>

<p>※ 해당 게시글은 소정의 원고료를 받아 작성되었습니다.</p>
<p>👇🏻 제로베이스 데이터 취업 스쿨 바로 가기
<a href="https://zero-base.co.kr/category_data_camp/school_DS">https://zero-base.co.kr/category_data_camp/school_DS</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Git] 100MB가 넘는 대용량 파일 깃허브에 업로드하기 / 동료와 깃허브 연동하기 / 외장하드에서 깃허브 업로드하기]]></title>
            <link>https://velog.io/@castle_mi/Git-100MB%EA%B0%80-%EB%84%98%EB%8A%94-%EB%8C%80%EC%9A%A9%EB%9F%89-%ED%8C%8C%EC%9D%BC-%EA%B9%83%ED%97%88%EB%B8%8C%EC%97%90-%EC%97%85%EB%A1%9C%EB%93%9C%ED%95%98%EA%B8%B0-%EB%8F%99%EB%A3%8C%EC%99%80-%EA%B9%83%ED%97%88%EB%B8%8C-%EC%97%B0%EB%8F%99%ED%95%98%EA%B8%B0-%EC%99%B8%EC%9E%A5%ED%95%98%EB%93%9C%EC%97%90%EC%84%9C-%EA%B9%83%ED%97%88%EB%B8%8C-%EC%97%85%EB%A1%9C%EB%93%9C%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@castle_mi/Git-100MB%EA%B0%80-%EB%84%98%EB%8A%94-%EB%8C%80%EC%9A%A9%EB%9F%89-%ED%8C%8C%EC%9D%BC-%EA%B9%83%ED%97%88%EB%B8%8C%EC%97%90-%EC%97%85%EB%A1%9C%EB%93%9C%ED%95%98%EA%B8%B0-%EB%8F%99%EB%A3%8C%EC%99%80-%EA%B9%83%ED%97%88%EB%B8%8C-%EC%97%B0%EB%8F%99%ED%95%98%EA%B8%B0-%EC%99%B8%EC%9E%A5%ED%95%98%EB%93%9C%EC%97%90%EC%84%9C-%EA%B9%83%ED%97%88%EB%B8%8C-%EC%97%85%EB%A1%9C%EB%93%9C%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 30 Jan 2024 06:37:37 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요 여러분!!
제로베이스 데이터스쿨을 종강하고 저는 
그동안 프로젝트했던 코드들을 깃허브에 정리하고 있습니다 :) </p>
<p>깃허브에 제일 문제가 되었던 상황을 소개해보려고 해요.</p>
<p>대용량 파일을 깃허브에 업로드하려고 하니 아래와 같이 에러가 뜨더라구요 😭 
<img src="https://velog.velcdn.com/images/castle_mi/post/185d2f30-4241-4c02-9084-a49f0e345148/image.png" alt=""></p>
<p>해결 방법을 바로 보고싶으신 분들은 목차 <strong>3. 깃허브 연동부터 업로드까지 전체 코드 정리</strong>를 참고 해주세요!</p>
<br>

<h1 id="0-동료들과-github-연동하기collaborators">0. 동료들과 Github 연동하기(Collaborators)</h1>
<p>먼저 같이 프로젝트 한 동료들과 Github를 연동하고 싶어서
Repository를 제가 만들고 동료들을 Collaborators로 추가해주었습니다.
<img src="https://velog.velcdn.com/images/castle_mi/post/cd6c0e7b-ea76-4bfe-9f76-f0024ce16cd0/image.png" alt="">
<strong>1. Repository의 오른쪽 상단 점 3개 클릭 - Settings 클릭</strong></p>
<br>

<p><img src="https://velog.velcdn.com/images/castle_mi/post/af3dc34a-35ed-483e-aaa1-da9dce5dc5fd/image.png" alt=""></p>
<p><strong>2. 왼쪽 Collaborators 클릭 - 오른쪽 아래 초록색 Add people 클릭</strong></p>
<br>

<p><img src="https://velog.velcdn.com/images/castle_mi/post/80ae33d0-1ce9-454c-afaf-aa05e13fd5a3/image.png" alt="">
<strong>3. 동료의 깃허브 이름을 검색 후 초록색 Select a collaborator above 클릭</strong></p>
<br>

<p><strong>4. 동료는 자신에게 온 알람을 수락!하기</strong></p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/d7327553-9503-4c43-9f2d-7a7d62a4bb10/image.png" alt="">
<strong>5. 동료들의 이름 아래에 &#39;Collaborator&#39;라 뜨면 정상적으로 설정은 완료된 것</strong></p>
<br>

<p><a href="https://hyoje420.tistory.com/41">🔎 참고 자료 </a></p>
<p>이것저것 구글링해보면 위 과정까지 하면
자동적으로 Collaborator로 등록이 되는거라고 하는데
동료들의 Repository에는 안 뜨는 현상이 있다고 하더라구요.</p>
<p>이 경우 동료들이 자신의 컴퓨터에 clone하고 commit 하면 정상적으로 동료들의 Repository에도 뜬다고 합니다! </p>
<p><br><br><br></p>
<h1 id="1-외장하드에서-깃허브에-업로드하기">1. 외장하드에서 깃허브에 업로드하기</h1>
<p>저는 지금 노트북 용량이 부족해서 외장하드로 작업을 하고 있었습니다.</p>
<p>그래서 깃허브도 외장하드로 연동하고 싶었죠..
깃허브에 업로드 하기 위해 외장하드 폴더에서 <code>git add .</code> 와 같은 코드를 실행시키면 아래와 같은 문구가 뜨실 겁니다.</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/2bc6ffa2-c7c8-403f-bc50-9781006c9cd5/image.png" alt=""></p>
<p><code>git config --global --add safe.directory &#39;폴더 경로&#39;</code></p>
<p>그러면 나타난 문구 고대로 복사 붙여넣기 해서 실행시켜주시면 되어요!</p>
<p>구글링해보았는데 과거 깃허브에 어떤 문제가 있었어서 Repository에 업로드 하기 전 사용해도 되는 폴더인지 안전 검사를 하기 위함이랍니다!!</p>
<p><a href="https://velog.io/@blackbean99/%EC%99%B8%EC%9E%A5%ED%95%98%EB%93%9C%EC%97%90%EC%84%9C-github-%EC%82%AC%EC%9A%A9%ED%95%A0-%EC%88%98-%EC%9E%88%EB%82%98%EC%9A%94">🔎 참고 자료</a></p>
<p><br><br><br></p>
<h1 id="2-대용량-파일-업로드하기">2. 대용량 파일 업로드하기</h1>
<p>깃허브의 하나의 난관을 넘었는데 다시 또 🚵🏻‍♀️산이 찾아왔죠... ㅎㅎ </p>
<p>제 파일은 현재 157MB 정도 되었는데 깃허브에는 최대 100MB 까지만 올라간다고 하더라구요?? </p>
<h2 id="2-1-git-lfs-설치">2-1. Git LFS 설치</h2>
<p>찾아보면 다들 <strong>Git Large File Storage</strong> 인 <strong>lfs</strong>를 쓰라고 나옵니다.</p>
<br>

<p><a href="https://git-lfs.com/">📌 Git Large File Storage Download Page</a>
<img src="https://velog.velcdn.com/images/castle_mi/post/b3f2cd9f-0d40-4965-ada5-65bf6c86c74e/image.png" alt=""></p>
<p>Window인 저는 위 사이트를 이용해 다운 받아주었고</p>
<p><code>git lfs install</code> 이라는 코드를 실행시켰을 때 아래와 같이 나온다면 정상적으로 다운받아진거라고 합니다!
<img src="https://velog.velcdn.com/images/castle_mi/post/301a83a5-05ff-4745-bf4e-c2561a643a2f/image.png" alt=""></p>
<p><a href="https://docs.github.com/ko/repositories/working-with-files/managing-large-files/installing-git-large-file-storage">🔎 Github Docs </a>
<br></p>
<h2 id="2-2-깃허브-업로드-코드">2-2. 깃허브 업로드 코드</h2>
<p>그리고 아래 코드를 따라서 실행시키면 됩니다.
먼저 제가 업로드할 내용에 대해 소개해드리자면 
<code>2. EDA</code> 폴더 안에 <code>2. EDA.ipynb</code> 파일이 있어 <code>2. EDA</code> 폴더 자체를 업로드하려고 했습니다!</p>
<p><strong>📌 대용량 파일을 깃허브에 업로드하는 코드 전체</strong></p>
<pre><code class="language-bash">// Step1. lfs로 관리할 대용량 파일 설정해주기
$ git lfs track 파일명

// git lfs track &quot;2. EDA/2. EDA.ipynb&quot;
// 저는 폴더 안에 대용량 파일이 있기 때문에
// 위와 같이 경로까지 설정해주었습니다.


// Step2. 변경된 내용 스테이징
$ git add .gitattributes
$ git add 파일명

// git add &quot;2. EDA/2. EDA.ipynb&quot;

// Step3. 커밋
$ git commit -m &quot;커밋 메세지&quot;

// Step4. 푸시
$ git push origin main
</code></pre>
<p>제가 엄청 헤맸던 곳은 바로 Step1 부분이었습니다.
저 부분에서 폴더를 업로드할 거라고 <code>git lfs track &quot;2. EDA&quot;</code> 라고 실행시켰더니 <code>gitattributes</code> 파일은 생성되었지만 계속 똑같은 에러가 뜨더라구요.</p>
<p>여러분은 🌟🌟🌟 무조건 대용량 파일 그 자체!!! 경로🌟🌟🌟를 적어주셔야 합니다.</p>
<br>

<h2 id="2-3-코드를-여러-번-실행시킨-내가-자주-사용한-코드">2-3. 코드를 여러 번 실행시킨 내가 자주 사용한 코드</h2>
<p>이게 계속 실패하다보면 여러 코드를 사용하게 되고
이 때 코드가 얽히게 되면서 실행이 안 되는 경우도 있다.</p>
<p>여러 번 시도하면서 코드가 얽히지 않도록 하기 위해 
자주 사용했던 코드를 적어보자면...</p>
<h3 id="이미-파일을-commit-했을-때">이미 파일을 commit 했을 때</h3>
<p>commit을 취소하고 해당 파일을 staged 상태로 바꿔주기</p>
<pre><code class="language-bash">$ git log //commit 목록 확인
$ git reset --soft HEAD^ // commit을 취소하고 해당 파일들은 staged 상태로</code></pre>
<h3 id="추적-그만하라고-알려주기">추적 그만하라고 알려주기</h3>
<pre><code class="language-bash">$ git lfs untrack &quot;파일 경로&quot;
</code></pre>
<br>

<h2 id="2-4-업로드-완료">2-4. 업로드 완료</h2>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/19578a08-f7a0-4f93-98bd-2b0e2adc3997/image.png" alt=""></th>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/06b01624-11c9-4dd2-9464-6aade434c607/image.png" alt=""></th>
</tr>
</thead>
<tbody><tr>
<td>아래 두 참고 자료를 통해서 이것 저것 시도해보고 드디어 업로드를 완료했다🥰 업로드 되는 순간 돌아가는 코드는 첫 느낌부터 달랐다는...!! (이번엔 무조건 된다...!는 느낌이 뽝!! 왔어요 ㅎㅎ)</td>
<td></td>
</tr>
</tbody></table>
<p><a href="https://min-kyung.tistory.com/19">🔎 Git LFS 간단 정리 사이트 </a>
<a href="https://newsight.tistory.com/330">🔎 Git LFS 사용시 발생하는 여러 오류 코드들에 대한 해결 방법 사이트 </a></p>
<p><br><br><br></p>
<h1 id="3-깃허브-연동부터-업로드까지-전체-코드-정리">3. 깃허브 연동부터 업로드까지 전체 코드 정리</h1>
<pre><code class="language-bash"># Repository 복제하기 
// Step0. 깃허브 Repository를 clone 할 폴더로 이동하기

// Step1. user.name, user.email 입력해주기
$ git config --global user.name castlemi99
$ git config --global user.email nabi4442@naver.com

// Step1-1. 외장하드의 경우 해당 경로가 안전하다는걸 알려주기
$ git config --global --add safe.directory &quot;외장하드 경로&quot;
    // 나의 경우
    // $ git config --global --add safe.directory &quot;D:/제로베이스데이터스쿨/Github_castlemi99&quot;

// Step2. Repository clone하기
$ git clone Repository주소
    // 나의 경우
    // $ git clone https://github.com/castlemi99/Final_Project.git



# 대용량 파일 업로드하기 
// Step3. Git LFS 설치 및 설치 확인하기
$ git lfs install

// Step4. Git LFS 파일 추적 설정
$ git lfs track 파일명
    // 나의 경우
    // $ git lfs track &quot;2. EDA/2. EDA.ipynb&quot;

// Step5. 변경된 내용 스테이징
$ git add .gitattributes
$ git add 파일명
    // 나의 경우
    // $ git add .gitattributes
    // $ git add &quot;2. EDA/2. EDA.ipynb&quot;

// Step6. 커밋
$ git commit -m &quot;커밋 메세지&quot;
    // 나의 경우
    // $ git commit -m &quot;EDA Update, 240130&quot;

// Step7. 푸시
$ git push origin main
// 아래와 같이 동작하면서 업로드 됨
Uploading LFS objects: 100% (1/1), 161 MB | 352 KB/s, done.
Enumerating objects: 6, done.
Counting objects: 100% (6/6), done.
Delta compression using up to 8 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (5/5), 583 bytes | 583.00 KiB/s, done.
Total 5 (delta 0), reused 0 (delta 0), pack-reused 0
To https://github.com/castlemi99/Final_Project.git
   51c0d3b..678efa6  main -&gt; main</code></pre>
<p><br><br><br></p>
<p>원래 VSCode에서 바로 깃허브에 연동되게 하기 위해서
이것 저것 찾아보았는데 결국 대용량 파일 때문에 코드를 통해 업로드해따.. 😂 </p>
<p>연동하기 위해 참고한 사이트도 첨부해둬야지!!(언젠간 쓰겠지...)
<a href="https://artistjay.tistory.com/72">🔎 참고 자료 1</a>
<a href="https://choco-life.tistory.com/44">🔎 참고 자료 2</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[GPT] 자연어처리 모델 이론 및 Hugging Face 이용 방법 ]]></title>
            <link>https://velog.io/@castle_mi/GPT</link>
            <guid>https://velog.io/@castle_mi/GPT</guid>
            <pubDate>Sat, 11 Nov 2023 05:31:12 GMT</pubDate>
            <description><![CDATA[<p>✍🏻 8일 공부 이야기.</p>
<p><br><br></p>
<h1 id="개요-및-기초">개요 및 기초</h1>
<h2 id="특징">특징</h2>
<ul>
<li>자연어 데이터의 특징</li>
</ul>
<ol>
<li>문장을 구성하고 있는 단어들의 위치가 변해서는 안됨</li>
<li>단어들 간의 관계가 중요하고 하나의 단어만 바뀌거나 추가되어도 전혀 다른 의미를 가질 수 있음</li>
<li><code>Bert</code>는 특히 자연어 데이터에 특화죈 데이터프레임 워크</li>
</ol>
<br>

<ul>
<li>GPT를 구성하고 있는 모델은 <code>Transformer</code>에서 가져온 것이므로 <code>Transformer</code>의 구성 모듈에 대해 자세히 배울 예정</li>
</ul>
<br>

<ul>
<li><p>자연어 데이터의 토큰화
<img src="https://velog.velcdn.com/images/castle_mi/post/53af022a-e6e9-4d86-8bc7-1a2829af1323/image.png" alt=""></p>
<p>🤔 어떻게 쪼갤 것인가? 
문자별로? 공백 별로? 구두점(&#39; / ! 등) 별로?</p>
<p>일단 최소한의 의미가 있는 것 별로 쪼개야한다는 생각해볼 수 있을 것이다. 그 관점에서 보면</p>
<ul>
<li>문자 별(Character) : 문장의 시계열 길이가 너무 늘어나고, 각각의 문자는 의미를 가지고 있지 않으므로 결국 단어로 표현을 해야한다는 단점이 있음</li>
<li>단어 별(Word) : 경우의 수가 너무 많으며 특히 사전에 없는 단어(이모티콘, 신조어 등)가 생길 위험이 있음</li>
</ul>
<p>위와 같은 생각을 할 수 있을 것이다.
각자가 생각하는 최소한의 의미가 다 다르기 때문에 이미 만들어진 모델을 불러와서 사용한다고 해도 결과가 완전 다를 수 있다.</p>
<p>따라서 <strong>GPT에서는 전처리를 어떻게 했는지부터 하나하나 세심하게 볼 필요가 있다.</strong></p>
</li>
</ul>
<br>

<ul>
<li><p>자연어 처리 Task</p>
<ul>
<li><p>자연어 처리 모델은 어떤 흐름을 가지고 있을까?
먼저, 하나의 문장을 여러 개로 나누고 나눈 토큰들의 결합 분포로 문장에 대해서 확률을 계산한다. 이를 통해</p>
<ul>
<li>감정 분석 : 해당 문장의 긍/부정의 확률 추출</li>
<li>번역 : 해당 문장을 번역</li>
<li>중간 단어를 빈칸으로 보고 해당 단어에 무엇이 들어올지 예측</li>
</ul>
<p>위와 같은 다양한 작업을 할 수 있다.</p>
</li>
</ul>
</li>
</ul>
<p><br><br></p>
<h2 id="기존-연구-간략-소개">기존 연구 간략 소개</h2>
<p>자연어 처리에서 또 중요하게 생각한 것은 context(문맥)이다. 한 문장을 context를 가지고 있는 어떤 벡터로 표현하고 싶었는데 이미지나 다른 데이터와 다르게 각각의 순서가 너무 중요했다.
<img src="https://velog.velcdn.com/images/castle_mi/post/f039d8c0-84fb-4401-8f17-4b05bba59fd9/image.png" alt=""></p>
<p>그래서 문맥을 알아내는 Encoder를 학습했다면 다양한 Task에 적용이 가능했다.</p>
<p>기존 연구(RNN, LSTM)에서 문장은 순서를 가지고 있으니, 문장의 처음부터 끝까지 순서대로 입력을 받아서 최종적으로 벡터를 생성해서 사용했었다.
<img src="https://velog.velcdn.com/images/castle_mi/post/56cb140c-2003-4f0a-a468-95ffb1e72b57/image.png" alt=""></p>
<p>하지만 중요한 단어들이 앞 부분에 있다면 순서대로 입력받는 와중에 정보를 잃어버리지 않을까? 하는 의문을 해결하기 위해 <strong>각각의 단어에 대해 Attention을 주기로 했다.</strong></p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/307358ef-ac1a-4b5c-b65e-3b1f2f1b1299/image.png" alt="">
--|--|</p>
<p>그래서 <code>attention score</code>라는 것이 나왔는데 
Apple이라는 단어를 번역한다고 했을 때 &#39;사과를&#39; 이라는 단어와의 유사 확률이 가장 높게 나올 것이며 가장 높은 확률을 가지는 단어에 Attention하면서 이후 과정을 진행하게 되는 것이다.</p>
<p><br><br></p>
<h1 id="transformer">Transformer</h1>
<h2 id="positional-encoding">Positional Encoding</h2>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/6e54bf18-138e-46c9-8ed8-c067b93b04a9/image.png" alt=""></p>
<p>RNN은 순서를 처리할 수 있었지만, Transformer는 병렬처리를 하기 때문에 순서를 알 수 없었다. 그래서 <code>Positional Encoding</code>을 적용시켰으며 <code>Multi head attention</code>이라는 핵심 모듈을 이용했다.
<code>Multi head attention</code>은 <code>Self-Attention</code> 매커니즘을 이용하며 <code>key query value</code> 개념을 이용했다.</p>
<h3 id="어떻게-줄까">어떻게 줄까?</h3>
<p>👀 먼저, 병렬적으로 데이터를 처리하는 Transformer에서는 순서를 고려하기 위해 <code>Positional Encoding</code>을 적용하게 되었다고 했다.</p>
<p>어떻게 줄까에 대해 여러 방법들이 나왔었는데 아래와 같은 문제점들이 있었다.</p>
<ol>
<li><strong>단어 순서대로 카운팅</strong> : 숫자가 너무 커져서 weight 학습 시 어려움</li>
<li><strong>단어 순서대로 카운팅 후 정규화</strong> : weight는 안정적이지만 문장의 단어 개수에 따라 같은 자릿수에 있는 단어에 다른 값이 할당됨</li>
<li><strong>단어 순서대로 벡터로 표현</strong> : 문장의 단어 개수에 영향을 받지 않지만 단어 순서끼리의 거리가 다르게 됨</li>
</ol>
<blockquote>
<p>🤔 숫자가 너무 커지지 않으며, 같은 자릿수의 단어에는 같은 값이 할당되고, 문장 내 단어의 상대적인 위치가 같을 수 있는 방법이 없을까?</p>
</blockquote>
<p>위와 같은 고민으로 탄생된게 <code>Sinusoidal Encoding</code> 방법이다. 
<img src="https://velog.velcdn.com/images/castle_mi/post/11dab0c8-2452-4a88-b0f5-15b3742a40b6/image.png" alt="">
이 방법은 위 3가지 고민을 해결하며 단어의 순서에 따른 문맥을 이해할 수 있게 되었다. 위 사진에서 t는 문장 내 단어의 위치이고 i는 임베딩 차원의 인덱스를 뜻한다.</p>
<p>코드는 아래와 같이 구현할 수 있다.
<img src="https://velog.velcdn.com/images/castle_mi/post/f5df5a0e-8856-4025-8738-9d535fb6e117/image.png" alt=""></p>
<p>솔직히... 아직 이해가 완벽하게 된 상태가 아닌 상태이긴 하다...  🥲</p>
<br>

<h2 id="multi-head-self-attention">Multi head self attention</h2>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/6d081d3f-fc96-400c-926d-3d8f014b665c/image.png" alt=""></th>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/e52d2f88-aaff-4e7d-80d2-cb71609619fd/image.png" alt=""></th>
</tr>
</thead>
</table>
<ul>
<li>key , query, value 개념을 기본으로</li>
<li>scaled dot-product attention 이용
: 단어에 대한 벡터는(Q) 주어진 단어들에 대해서 유사한 정도만큼(K) 고려하고 각 주어진 단어들은 V만큼의 중요도를 가진다.</li>
</ul>
<br>

<p><strong>📌 코드로 이해하기</strong></p>
<pre><code class="language-python">class MultiHeadAttentionLayer(nn.Module):
    def __init__(self, hidden_dim, n_heads, dropout_ratio, device):
        super().__init__()

        assert hidden_dim % n_heads == 0

        self.hidden_dim = hidden_dim # 임베딩 차원
        self.n_heads = n_heads # 헤드(head)의 개수: 서로 다른 어텐션(attention) 컨셉의 수
        self.head_dim = hidden_dim // n_heads # 각 헤드(head)에서의 임베딩 차원

        self.fc_q = nn.Linear(hidden_dim, hidden_dim) # Query 값에 적용될 FC 레이어
        self.fc_k = nn.Linear(hidden_dim, hidden_dim) # Key 값에 적용될 FC 레이어
        self.fc_v = nn.Linear(hidden_dim, hidden_dim) # Value 값에 적용될 FC 레이어

        self.fc_o = nn.Linear(hidden_dim, hidden_dim)

        self.dropout = nn.Dropout(dropout_ratio)

        self.scale = torch.sqrt(torch.FloatTensor([self.head_dim])).to(device)

    def forward(self, query, key, value, mask = None):

        batch_size = query.shape[0]

        # query: [batch_size, query_len, hidden_dim]
        # key: [batch_size, key_len, hidden_dim]
        # value: [batch_size, value_len, hidden_dim]

        Q = self.fc_q(query)
        K = self.fc_k(key)
        V = self.fc_v(value)

        # Q: [batch_size, query_len, hidden_dim]
        # K: [batch_size, key_len, hidden_dim]
        # V: [batch_size, value_len, hidden_dim]

        # hidden_dim → n_heads X head_dim 형태로 변형
        # n_heads(h)개의 서로 다른 어텐션(attention) 컨셉을 학습하도록 유도
        Q = Q.view(batch_size, -1, self.n_heads, self.head_dim).permute(0, 2, 1, 3)
        K = K.view(batch_size, -1, self.n_heads, self.head_dim).permute(0, 2, 1, 3)
        V = V.view(batch_size, -1, self.n_heads, self.head_dim).permute(0, 2, 1, 3)

        # Q: [batch_size, n_heads, query_len, head_dim]
        # K: [batch_size, n_heads, key_len, head_dim]
        # V: [batch_size, n_heads, value_len, head_dim]

        # Attention Energy 계산
        energy = torch.matmul(Q, K.permute(0, 1, 3, 2)) / self.scale

        # energy: [batch_size, n_heads, query_len, key_len]

        # 마스크(mask)를 사용하는 경우
        if mask is not None:
            # 마스크(mask) 값이 0인 부분을 -1e10으로 채우기
            energy = energy.masked_fill(mask==0, -1e10)

        # 어텐션(attention) 스코어 계산: 각 단어에 대한 확률 값
        attention = torch.softmax(energy, dim=-1)

        # attention: [batch_size, n_heads, query_len, key_len]

        # 여기에서 Scaled Dot-Product Attention을 계산
        x = torch.matmul(self.dropout(attention), V)

        # x: [batch_size, n_heads, query_len, head_dim]

        x = x.permute(0, 2, 1, 3).contiguous()

        # x: [batch_size, query_len, n_heads, head_dim]

        x = x.view(batch_size, -1, self.hidden_dim)

        # x: [batch_size, query_len, hidden_dim]

        x = self.fc_o(x)

        # x: [batch_size, query_len, hidden_dim]

        return x, attention</code></pre>
<br>

<h2 id="layer-norm">Layer norm</h2>
<p><strong>👀 Batch Normalization vs Layer Normalization</strong>
<img src="https://velog.velcdn.com/images/castle_mi/post/79a72d11-fa08-48b7-8dac-6c8de8e7a4fe/image.png" alt=""></p>
<ul>
<li>Batch norm : sample 들의 feature 별 평균과 분산 -&gt; batch size에 따라서 성능 변화가 심함</li>
<li>Layer norm : 각 batch 에 대해서 feature들의 평균과 분산</li>
</ul>
<br>

<p><strong>📌 코드로 이해하기</strong></p>
<pre><code class="language-python">class LayerNorm(nn.Module):
  def __init__(self, d_model, eps = 1e-8):
      super(LayerNorm, self).__init__()
      self.gamma = nn.Parameter(torch.ones(d_model))
      self.beta = nn.Parameter(torch.zeros(d_model))
      self.eps = eps

  def forward(self, x):
        # -1 : 마지막 dim(= feature)에 대한 평균
      mean = x.mean(-1, keepdim = True)
      std = x.std(-1, keepdim = True)
      return self.gamma * (x - mean) / (std + self.eps) + self.beta
</code></pre>
<br>


<p><strong>🖱️ Transformer 코드 이해하기</strong>
<a href="https://github.com/ndb796/Deep-Learning-Paper-Review-and-Practice/blob/master/code_practices/Attention_is_All_You_Need_Tutorial_(German_English).ipynb">https://github.com/ndb796/Deep-Learning-Paper-Review-and-Practice/blob/master/code_practices/Attention_is_All_You_Need_Tutorial_(German_English).ipynb</a></p>
<p><br><br></p>
<h1 id="bert--gpt">BERT , GPT</h1>
<p>자연어 데이터는 다양한 Label이 요구되고 format도 매우 복잡하게 되어있다.
의료 데이터셋과 비교하면 수많은 텍스트 데이터들이 레이블이 없이 존재하는 경우가 많은데 주어진 문장에 대한 함축적인 문맥을 이해할 수 있다면 우리는 다양한 Task에 적용할 수 있을 것이다.</p>
<h2 id="gpt-1">GPT-1</h2>
<blockquote>
<ul>
<li>비지도 학습 기반과 지도 학습 기반을 결합한 semi-supervised learning</li>
</ul>
</blockquote>
<ul>
<li>이를 통해 다양한 자연어 Task에서 지도 학습 만으로도 좋은 성능을 보이는 범용적인 자연어 representation을 학습시킴</li>
<li>2 stage로 구성되어있으며 Transformer의 Decoder 구조를 사용</li>
<li>기존 RNN 대비 좋은 성능</li>
</ul>
<br>

<p><strong>📌 GPT-1의 2 stage</strong></p>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/2b7f420d-2dba-4fa3-b7e5-2ff60dc819dc/image.png" alt=""></th>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/693fd248-36d8-4cfd-878d-bf94adc0cde5/image.png" alt=""></th>
</tr>
</thead>
</table>
<p><br><br></p>
<h2 id="bert">BERT</h2>
<blockquote>
<ul>
<li>Wiki &amp; Book data와 같은 대용량 unlabeled data 로 pre-training 시킨 후 특정 Task 에 Transfer learning을 함</li>
</ul>
</blockquote>
<ul>
<li>GPT와 달리 새로운 네트워크를 붙이지 않고 Fine-tunning 만 진행</li>
<li>GPT에서의 Unsupervised pre-trainning 을 BERT에서는 Masked Language Model과 Next sentence prediction을 사용<ul>
<li>Next sentence prediction : 문장 간 관계를 알아내기 위한 Task</li>
</ul>
</li>
<li>Pre-training 프로세스는 GPT-1과 같음</li>
</ul>
<p><br><br></p>
<h2 id="gpt-2">GPT-2</h2>
<blockquote>
<ul>
<li>Fine tunning 없이</li>
</ul>
</blockquote>
<ul>
<li>모델 자체는 GPT-1과 크게 다르지 않음</li>
<li>WebText 데이터를 구축<ul>
<li>이 대용량 데이터셋에 LM 모델을 학습했을 때 supervision 없이도 다양한 Task 처리가 가능해짐</li>
</ul>
</li>
<li>Byte pair Encoding을 활용하여 Out of Vocabulary 문제 해결</li>
</ul>
<p><br><br></p>
<h2 id="gpt-3">GPT-3</h2>
<blockquote>
<ul>
<li>GPT-2 대비 Self attention layer를 굉장히 많이 쌓아 parameter 수를 대폭 늘림</li>
</ul>
</blockquote>
<ul>
<li>GPT-2에서 사용하는 Zero shot learning framwork 확장</li>
</ul>
<br>

<p><strong>📌 정리</strong>
<img src="https://velog.velcdn.com/images/castle_mi/post/3b623616-61bd-4a67-8994-b4b846d26bf4/image.png" alt=""></p>
<p><br><br></p>
<h1 id="hugging-face-라이브러리">Hugging face 라이브러리</h1>
<h2 id="소개-및-사용법">소개 및 사용법</h2>
<p><strong>🖱️ Hugging Face 공식 사이트</strong></p>
<ul>
<li><a href="https://huggingface.co/">https://huggingface.co/</a></li>
<li><a href="https://github.com/huggingface">https://github.com/huggingface</a></li>
</ul>
<br>

<p><strong>📌 install</strong>
<code>pip install transformers</code>
<code>pip install transformers[sentencepiece]</code>
<code>pip install datasets</code></p>
<br>

<h3 id="다양한-task">다양한 Task</h3>
<p><code>from transformers import pipeline</code>
<code>pipeline(&#39;사용할 Task 이름&#39;, model = &#39;모델명&#39;)</code></p>
<br>

<p><strong>감성 분석(sentiment-analysis)</strong>
: 문장의 긍/부정 예측</p>
<pre><code class="language-python"># sentiment-analysis 작업을 하는 pipeline 불러오기
classifier = pipeline(&#39;sentiment-analysis&#39;)

classifier(&quot;I&#39;ve been waiting for a HuggingFace course my whole life.&quot;)
# 해당 문장에 대한 긍/부정과 스코어값 반환</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>[{&#39;label&#39;: &#39;POSITIVE&#39;, &#39;score&#39;: 0.9598048329353333}]</p>
<br>

<p><strong>제로샷 learning</strong>
: 새로운 클래스에 대한 학습 데이터가 없는 상황에서도 해당 클래스를 인식하고 분류할 수 있는 학습 방법</p>
<pre><code class="language-python">classifier = pipeline(&#39;zero-shot-classification&#39;)

# 레이블이 없기 때문에 우리가 주고 학습시키면 됨

classifier(
    &quot;This is a course about the transformers library&quot;,
    candidate_labels = [&#39;education&#39;, &#39;politics&#39;, &#39;business&#39;] # 우리가 원하는 라벨에 대한 출력 가능

)</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>{&#39;sequence&#39;: &#39;This is a course about the transformers library&#39;,
 &#39;labels&#39;: [&#39;education&#39;, &#39;business&#39;, &#39;politics&#39;],
 &#39;scores&#39;: [0.9192408919334412, 0.06077827885746956, 0.01998082548379898]}</p>
<br>

<pre><code class="language-python"># 문장 속 단어는 높게 추출
classifier(
    &quot;this is a course about the transformers library&quot;,
    candidate_labels = [&#39;education&#39;, &#39;politics&#39;, &#39;business&#39;,
                       &#39;library&#39;, &#39;game&#39;, &#39;play&#39;]

)</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>{&#39;sequence&#39;: &#39;this is a course about the transformers library&#39;,
 &#39;labels&#39;: [&#39;library&#39;, &#39;education&#39;, &#39;business&#39;, &#39;play&#39;, &#39;game&#39;, &#39;politics&#39;],
 &#39;scores&#39;: [0.5048014521598816,
  0.4317719638347626,
  0.026262251660227776,
  0.015142401680350304,
  0.011997081339359283,
  0.010024827904999256]}</p>
<br>

<p><strong>Text-generation</strong>
: 주어진 문맥이나 지시에 따라 새로운 텍스트를 생성</p>
<pre><code class="language-python">generator = pipeline(&#39;text-generation&#39;)

generator(&quot;In this course, we will teach you how to &quot;)</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>Setting <code>pad_token_id</code> to <code>eos_token_id</code>:50256 for open-end generation.
[{&#39;generated_text&#39;: &#39;In this course, we will teach you how to ute asynchronously in the time-honoured way, creating simple, effective, and highly interactive apps and applications to help you be productive in the most productive time of your life—while simultaneously&#39;}]</p>
<br>

<pre><code class="language-python"># 다양한 옵션을 지정할 수도 있고
generator(&quot;In this course, we will teach you how to &quot; , 
          # 다양한 옵션 지정 가능
          num_return_sequences = 10,
          max_length = 30
          )


# 리스트 형태로 문장을 넣어줄 수 있음
list_ = [&quot;In this course, we will teach you how to &quot; , 
         &quot;this is a course about the transformers library&quot;]

for sentence in list_:
    print(generator(sentence, num_return_sequences = 1, max_length = 50))</code></pre>
<br>

<p><strong>Mask filling</strong>
: 문장에서 일부 단어를 마스킹(가려서 숨김)한 후, 모델이 해당 마스킹된 단어를 예측하는 작업</p>
<pre><code class="language-python">unmasker = pipeline(&#39;fill-mask&#39;)

# 마스크할 부분을 &lt;mask&gt;로 지정
unmasker(&quot;This course will teach you all about &lt;mask&gt; models&quot; ,
          top_k = 5 # 몇 개를 추출할 것인지
         )</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/f89f5f69-bd39-4bb2-a0d6-61adafe4736f/image.png" alt=""></p>
<br>

<p><strong>Question Answering</strong>
: 문장에서 내가 물어보는 것에 대한 답변을 해주는 작업</p>
<pre><code class="language-python">question_answerer = pipeline(&#39;question-answering&#39;)

question_answerer(
    question = &#39;Where do I work?&#39;,
    context = &quot;My name is Sylvain and I work at Hugging Face in Brooklyn.&quot;
)</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>{&#39;score&#39;: 0.6385914087295532, &#39;start&#39;: 33, &#39;end&#39;: 45, &#39;answer&#39;: &#39;Hugging Face&#39;}</p>
<br>

<p><strong>Summarization</strong>
: 문장을 요약해주는 작업</p>
<pre><code class="language-python">summarizer = pipeline(&#39;summarization&#39;)
summarizer(
    &quot;&quot;&quot;
    On the technical side, it is sometimes claimed that Netflix epitomises a “vertical integration model”, operating across the entire chain of TV content production and distribution. But in reality it is great at outsourcing.
    For while Netflix has its own engineers and proprietary technology, including its famed recommendation system and algorithm, the company has designed its entire media delivery on third-party service infrastructure.
    Netflix shut its last data centre in 2016. Now everything the platform needs, from data storage to customer information and algorithms runs on Amazon’s web services.
    Outsourcing media delivery has enabled Netflix to avoid sinking cash in to global infrastructure, and instead focus on its core mission: members’ engagement across markets.
    The same goes with content production. Netflix &#39;originals&#39; are produced by the platform insofar has it pays for the entirety of the production costs. But the content is actually made by outside film and TV producers.
    This entails governing a vast network of suppliers, giving it extraordinary flexibility in a fast moving industry. In contrast, the likes of Disney+ have to rely more heavily on a limited range of production it is directly responsible for making.
    If Netflix does have a corporate weakness, it may be that its streaming service is not part of a large enterprise ecosystem. Amazon and Apple for example, sell goods to millions of customers and cleverly entwine different parts of their empires to deliver significant economies of scale.
    Despite this, Netflix’s approach embraces the challenges and opportunities a global market offers. Its outsourcing model makes it agile and able to pivot rapidly with a business model which enables it to thrive.
    The streaming industry – and television at large – is entering a period of adjustment because it needs to close the gap between content investment and revenue generation. Aided by some shrewd decisions and with a truly global outlook, the market leader appears to be in a good position to weather the storm.
    &quot;&quot;&quot;
)</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>No model was supplied, defaulted to sshleifer/distilbart-cnn-12-6 and revision a4f8f3e (<a href="https://huggingface.co/sshleifer/distilbart-cnn-12-6">https://huggingface.co/sshleifer/distilbart-cnn-12-6</a>).
Using a pipeline without specifying a model name and revision in production is not recommended.</p>
<br>

<p><strong>기계어 번역</strong>
: 문장을 다른 언어로 번역해주는 작업</p>
<pre><code class="language-python">translator = pipeline(&#39;translation&#39;, model = &#39;Helsinki-NLP/opus-mt-fr-en&#39;) # 프랑스어 -&gt; 영어

translator(&quot;Ce cours est produit par Hugging Face.&quot;)</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>[{&#39;translation_text&#39;: &#39;This course is produced by Hugging Face.&#39;}]</p>
<p><br><br></p>
<h1 id="tranning">Tranning</h1>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/f3c16cc6-eeaa-4b5c-8805-9208d9051662/image.png" alt=""></p>
<h2 id="tokenizer">Tokenizer</h2>
<p><strong>📌 데이터 준비</strong></p>
<pre><code class="language-python">from transformers import AutoTokenizer
checkpoint = &#39;distilbert-base-uncased-finetuned-sst-2-english&#39;

# 체크포인트의 tokenizer 불러오기
tokenizer = AutoTokenizer.from_pretrained(checkpoint)

raw_inputs = [
    &quot;I&#39;ve been waiting for a huggingface course my whole life&quot;,
    &quot;I hate this so much!&quot;
]

inputs = tokenizer(raw_inputs,
                   padding = True,
                   truncation = True,
                   return_tensors = &#39;pt&#39;)</code></pre>
<br>

<p>📌 모델</p>
<pre><code class="language-python">from transformers import AutoModelForSequenceClassification

model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
model.config.id2label  # {0: &#39;NEGATIVE&#39;, 1: &#39;POSITIVE&#39;}

outputs = model(**inputs)
outputs # 긍/부정이 확률값으로 나옴

# softmax 씌워서 최종 output 출력
import torch
predictions = torch.nn.functional.softmax(outputs.logits , dim = 1)
predictions</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>tensor([[4.8393e-02, 9.5161e-01],
        [9.9946e-01, 5.4418e-04]])</p>
<br>


<h3 id="model을-커스터마이징하고-저장하기">model을 커스터마이징하고 저장하기</h3>
<pre><code class="language-python">from transformers import BertConfig, BertModel

config = BertConfig()
config # BERT에 사용되고 있는 디폴트 옵션들

config.hidden_size = 48
model = BertModel(config)
model # 48로 교체된 것을 볼 수 있음

# 저장
model.save_pretrained(&#39;./test&#39;)</code></pre>
<br>


<h3 id="tokenizer를-할-때-꼭-해야하는-과정">Tokenizer를 할 때 꼭 해야하는 과정</h3>
<p>tokenizer를 할 때 의미있는 단어를 하나로 볼 수 있도록 학습시켜야하는 과정이 꼭 필요하다.
예를 들어 기계 번호(KT-13982)와 같은 경우 이상한 단어로 분리시키거나 Unknown을 띄우기 때문에 학습을 꼭 시켜주어야한다.</p>
<br>

<p><strong>📌 데이터 및 모델 준비</strong></p>
<pre><code class="language-python">from transformers import AutoTokenizer, AutoModelForSequenceClassification

checkpoint = &#39;distilbert-base-uncased-finetuned-sst-2-english&#39;
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = AutoModelForSequenceClassification.from_pretrained(checkpoint)

sequence = &quot;I&#39;ve been waiting for a HuggingFace course my whole life.&quot;</code></pre>
<br>

<p><strong>📌 1. 수동으로 토큰화하고 인덱스로 변환하는 과정</strong></p>
<pre><code class="language-python">#1 수동으로 토근화하고 인덱스로 변환하는 과정
tokens = tokenizer.tokenize(sequence)

ids = tokenizer.convert_tokens_to_ids(tokens)
input_ids = torch.tensor(ids)</code></pre>
<br>

<p>*<em>📌 2. return_tensors 옵션을 통해 자동으로 하는 방법 *</em></p>
<pre><code class="language-python">#2 return_tensors 옵션을 통해 자동으로 가능!
tokenized_inputs = tokenizer(sequence, return_tensors=&#39;pt&#39;)</code></pre>
<br>

<p><strong>방법 2를 더 추천하는 이유</strong></p>
<blockquote>
<p>#1을 사용하는 경우에는 몇 가지 문제점이 있을 수 있습니다.</p>
</blockquote>
<p><strong>1. 특수 문자 처리</strong>: #1에서는 직접 토큰화 과정을 거치기 때문에 특수 문자에 대한 처리를 별도로 해주어야 합니다. 예를 들어, 문장에 포함된 구두점이나 기호를 어떻게 처리할지 결정해야 합니다.
<strong>2. 토큰 제한</strong>: #1에서는 입력 시퀀스를 토큰화한 후 정수 인덱스로 변환하는 과정을 직접 수행하기 때문에, 모델이 처리할 수 있는 최대 토큰 개수를 초과하는 경우에는 문제가 발생할 수 있습니다. 이 경우에는 입력 시퀀스를 잘라내거나 다른 방법으로 처리해야 할 수 있습니다.
<strong>3. 특정 모델에 종속적</strong>: #1에서는 tokenizer와 모델을 따로 정의하고 초기화해야 합니다. 이는 특정 모델에 종속적이기 때문에 다른 모델을 사용하고자 할 때는 코드를 수정해야 합니다.</p>
<blockquote>
</blockquote>
<p>이러한 문제점들은 #2의 방식을 사용하는 경우에는 자동으로 처리되기 때문에 발생하지 않습니다. tokenizer 함수를 사용하면 특수 문자 처리, 토큰 제한 등의 과정이 내부적으로 처리되며, 특정 모델에 종속되지 않고 다양한 모델을 손쉽게 사용할 수 있습니다.</p>
<p><br><br></p>
<h2 id="실제-자연어-모델-학습-과정">실제 자연어 모델 학습 과정</h2>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/218f70fa-e914-42cf-bcb5-1de514603ae9/image.png" alt=""></th>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/ca16ce00-68d6-4d5c-8c25-5118abea9059/image.png" alt=""></th>
</tr>
</thead>
</table>
<p><strong>📌 데이터 준비</strong></p>
<pre><code class="language-python"># 데이터로드는 아래와 같이
from datasets import load_dataset
from transformers import AutoTokenizer, DataCollatorWithPadding

raw_datasets = load_dataset(&quot;glue&quot;, &#39;mrpc&#39;)
checkpoint = &#39;bert-base-uncased&#39;
tokenizer = AutoTokenizer.from_pretrained(checkpoint)</code></pre>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/56a321d0-ee1d-472f-a5fc-72e8d7cdb75a/image.png" alt="">
데이터셋 구성은 위와 같이 되어있는 것을 볼 수 있다.</p>
<br>

<p><strong>📌 지정한 컬럼에 대해 tokenizer 실시 및 컬럼명 변경</strong></p>
<pre><code class="language-python"># tokenizer function 지정
def tokenize_function(example):
    # sentence1, sentence2 에 대해 tokenizer 실시
    return tokenizer(example[&#39;sentence1&#39;], example[&#39;sentence2&#39;], truncation = True)

tokenized_datasets = raw_datasets.map(tokenize_function, batched = True)
# padding 지정
data_collator = DataCollatorWithPadding(tokenizer=tokenizer)

# 컬럼명 변경
tokenized_datasets = tokenized_datasets.remove_columns([&#39;sentence1&#39;, &#39;sentence2&#39;, &#39;idx&#39;])
tokenized_datasets = tokenized_datasets.rename_column(&#39;label&#39;, &#39;labels&#39;)
tokenized_datasets.set_format(&#39;torch&#39;)
tokenized_datasets[&#39;train&#39;].column_names</code></pre>
<br>

<p><strong>📌 데이터셋 분리</strong></p>
<pre><code class="language-python"># 데이터 분리
from torch.utils.data import DataLoader

train_dataloader = DataLoader(
    tokenized_datasets[&#39;train&#39;], shuffle=True, batch_size=8, collate_fn = data_collator
)

eval_dataloader = DataLoader(
    tokenized_datasets[&#39;validation&#39;], batch_size=8, collate_fn = data_collator
)</code></pre>
<br>

<p><strong>📌 학습</strong></p>
<pre><code class="language-python">from transformers import AutoModelForSequenceClassification

model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)

# optim
from transformers import AdamW
optimizer = AdamW(model.parameters(), lr = 5e-5)

# scheduler
from transformers import get_scheduler
num_epochs = 3
num_training_steps = num_epochs * len(train_dataloader)

lr_scheduler = get_scheduler(
    &#39;linear&#39;,
    optimizer=optimizer,
    num_warmup_steps=0,
    num_training_steps=num_training_steps
)

# gpu 설정
import torch
device = torch.device(&#39;cuda&#39;) if torch.cuda.is_available() else torch.device(&#39;cpu&#39;)
model.to(device)


# train
from tqdm import tqdm

progress_bar = tqdm(range(num_training_steps))

model.train()
for epoch in range(num_epochs):
    for batch in train_dataloader:
        batch = {k : v.to(device) for k, v in batch.items()}
        outputs = model(**batch)
        loss = outputs.loss
        loss.backward()

        optimizer.step()
        lr_scheduler.step()
        optimizer.zero_grad()
        progress_bar.update(1)</code></pre>
<br>

<p><strong>📌 평가</strong></p>
<pre><code class="language-python"># evaluation
from datasets import load_metric 

metric = load_metric(&#39;glue&#39;, &#39;mrpc&#39;)
model.eval()
for batch in eval_dataloader:
    batch =  {k : v.to(device) for k, v in batch.items()}
    with torch.no_grad():
        outputs = model(**batch)
    logits = outputs.logits
    predictions = torch.argmax(logits, dim = 1)
    metric.add_batch(predictions = predictions, references = batch[&#39;labels&#39;])
metric.compute()</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/afb1bfcd-2d58-4022-9b89-9160811ba2dd/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[PyTorch] 모델링 / 학습 / 모델 저장 및 불러오기 / 데이터 다루기]]></title>
            <link>https://velog.io/@castle_mi/PyTorch-%EB%AA%A8%EB%8D%B8%EB%A7%81-%ED%95%99%EC%8A%B5-%EB%AA%A8%EB%8D%B8-%EC%A0%80%EC%9E%A5-%EB%B0%8F-%EB%B6%88%EB%9F%AC%EC%98%A4%EA%B8%B0-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%8B%A4%EB%A3%A8%EA%B8%B0</link>
            <guid>https://velog.io/@castle_mi/PyTorch-%EB%AA%A8%EB%8D%B8%EB%A7%81-%ED%95%99%EC%8A%B5-%EB%AA%A8%EB%8D%B8-%EC%A0%80%EC%9E%A5-%EB%B0%8F-%EB%B6%88%EB%9F%AC%EC%98%A4%EA%B8%B0-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%8B%A4%EB%A3%A8%EA%B8%B0</guid>
            <pubDate>Sun, 05 Nov 2023 16:12:18 GMT</pubDate>
            <description><![CDATA[<p> ✍🏻5일 공부 이야기.</p>
<p><a href="https://github.com/castlemi99/ZeroBaseDataSchool/tree/main/PyTorch"><img src="https://velog.velcdn.com/images/castle_mi/post/74c69b04-9136-4cb9-b2ed-7d5b1cdc9be8/image.png" alt=""></a></p>
<p>오늘 공부한 실습 코드는 위 깃허브에 올려두었습니다 :) 사진 클릭시 해당 링크로 이동해요 !</p>
<h2 id="modeling-하는-방법-2가지">Modeling 하는 방법 2가지</h2>
<h3 id="방법-1-nnsequential">방법 1. nn.Sequential</h3>
<pre><code class="language-python">import torch
from torch import nn
import torch.nn.functional as F
import torchsummary

device = torch.device(&#39;cuda&#39; if torch.cuda.is_available() else &#39;cpu&#39;)

model = nn.Sequential(
    nn.Linear(784, 15),
    nn.Sigmoid(),
    nn.Linear(15, 10),
    nn.Sigmoid(),
).to(device)

# model의 정보를 좀 더 자세하고 이쁘게 출력 가능
torchsummary.summary(model, (784, ))</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/606c13cc-a90e-4d53-b9b5-6dfaa3714a1a/image.png" alt=""></p>
<br>

<h3 id="방법-2-nnmodule-sub-class">방법 2. nn.Module sub class</h3>
<pre><code class="language-python"># (1, 28, 28) input size 가정

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        # input 수를 지정해주어야함
        self.conv1 = nn.Conv2d(1, 20, kernel_size=3, padding=1)
        self.conv2 = nn.Conv2d(20, 50, kernel_size=3, padding=1)
        self.fc1 = nn.Linear(4900, 500)
        self.fc2 = nn.Linear(500, 10)

    def forward(self, x):
        x = F.relu(self.conv1(x)) #(28, 28)
        x = F.max_pool2d(x, 2, 2) #(14, 14)
        x = F.relu(self.conv2(x))
        x = F.max_pool2d(x, 2, 2) #(7, 7)

        x = x.view(-1, 4900)
        x = F.relu(self.fc1(x))
        x = F.log_softmax(self.fc2(x), dim=1)
        return x

model = Net()
torchsummary.summary(model, (1, 28, 28))</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/e5a20bc6-6d28-4d1e-ae04-068be8f5e3f0/image.png" alt=""></p>
<h3 id="resnet-구현">ResNet 구현</h3>
<pre><code># ResidualBlock

class ResidualBlock(nn.Module):
    def __init__(self, in_channel, out_channel):
        super(ResidualBlock, self).__init__()

        self.in_channel, self.out_channel = in_channel, out_channel

        self.conv1 = nn.Conv2d(in_channel, out_channel, kernel_size=1, padding=0)
        self.conv2 = nn.Conv2d(out_channel, out_channel, kernel_size=3, padding=1)
        self.conv3 = nn.Conv2d(out_channel, out_channel, kernel_size=1, padding=0)

        if in_channel != out_channel:
          # in_channel과 out_channel이 맞지 않을 시
          # 더해주기 전 한 번 더 channel을 맞춰주는 작업 실시
            self.shortcut = nn.Sequential(
                nn.Conv2d(in_channel, out_channel, kernel_size=1, padding=0)
            )
        else:
            self.shortcut = nn.Sequential()

    def forward(self, x):
        out = F.relu(self.conv1(x))
        out = F.relu(self.conv2(out))
        out = F.relu(self.conv3(out))
        out += self.shortcut(x)
        return out

class ResNet(nn.Module):
    def __init__(self, color=&#39;gray&#39;):
        super(ResNet, self).__init__()
        # 컬러에 따라 맨 첫번째 레이어을 다르게 작업
        if color == &quot;gray&quot;:
            self.conv1 = nn.Conv2d(1, 32, kernel_size=3, stride=1, padding=1)
        elif color == &quot;rgb&quot;:
            self.conv1 = nn.Conv2d(3, 32, kernel_size=3, stride=1, padding=1)

        self.resblock1 = ResidualBlock(32, 64)
        self.resblock2 = ResidualBlock(64, 64)

        self.avgpool = nn.AdaptiveAvgPool2d((1,1)) # averagepool
        self.fc1 = nn.Linear(64, 64)
        self.fc2 = nn.Linear(64, 10) # output에 맞게

    def forward(self, x):
        x = F.relu(self.conv1(x))
        x = F.max_pool2d(x, 2, 2)
        x = self.resblock1(x)
        x = self.resblock2(x)
        x = self.avgpool(x)
        x = torch.flatten(x,1)
        x = F.relu(self.fc1(x))
        x = self.fc2(x)
        x = F.log_softmax(x, dim=1)
        return x

model = ResNet()
torchsummary.summary(model, (1, 28, 28))</code></pre><blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/aae1beaf-6305-43a6-b460-e37a74286511/image.png" alt=""></p>
<p><br><br></p>
<h1 id="최적화">최적화</h1>
<h2 id="training">Training</h2>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/733473ed-fd1f-4001-923c-e9263f2f91b6/image.png" alt=""></p>
<p>tensorflow에 위와 같은 기능을 하는 것이 pytorch에도 있다.</p>
<h3 id="learning-rate-scheduler">Learning Rate Scheduler</h3>
<pre><code class="language-python">model = ResNet().to(device)
opt = optim.Adam(model.parameters(), lr = 0.03)


from torch.optim.lr_scheduler import ReduceLROnPlateau

scheduler = ReduceLROnPlateau(opt, mode = &#39;min&#39;, verbose = True)
# epoch이 진행됨에 따라 학습률을 줄여 안정적으로 수렴하도록 도움을 줌

# training loop

def train_loop(dataloader, model, loss_fn, optimizer, scheduler, epoch):
  model.train()
  size = len(dataloader)

  for batch, (x, y) in enumerate (dataloader):
    x, y = x.to(device), y.to(device)

    pred = model(x)
    loss = loss_fn(pred, y)

    optimizer.zero_grad()

    loss.backward()

    optimizer.step()

    if batch % 100 == 0:
      print(f&quot;Epoch {epoch} : [{batch} / {size}] loss : {loss.item()}&quot;)

  scheduler.step(loss)
  return loss.item()

# 학습  
for epoch in range(10):
  loss = train_loop(train_loader, model, F.nll_loss, opt, scheduler, epoch)
  print(f&quot;Epoch : {epoch} loss : {loss}&quot;)</code></pre>
<br>

<h2 id="model-save">Model save</h2>
<h3 id="weight만-저장">Weight만 저장</h3>
<pre><code class="language-python"># 저장
torch.save(model.state_dict(), &#39;model_weights.pth&#39;)

# 불러오기
model.load_state_dict(torch.load(&#39;model_weights.pth&#39;))</code></pre>
<h3 id="구조도-함께-저장">구조도 함께 저장</h3>
<pre><code class="language-python"># 저장
torch.save(model, &#39;model.pth&#39;)

# 불러오기
model = torch.load(&#39;model.pth&#39;)</code></pre>
<h3 id="어떻게-사용---checkpoint">어떻게 사용? -&gt; checkpoint</h3>
<pre><code class="language-python"># 저장 경로
checkpoint_path = &#39;checkpoint.pth&#39;

# 저장
torch.save({
    &#39;epoch&#39; : epoch,
    &#39;model_state_dict&#39; : model.state_dict(),
    &#39;optimizer_state_dict&#39; : opt.state_dict(),
    &#39;loss&#39; : loss
    }, checkpoint_path)

# 불러오기
checkpoint = torch.load(checkpoint_path)

# 학습시킬 모델 선언
model = ResNet().to(device)
optimizer = optim.SGD(model.parameters(), lr = 0.03)

# 다음에 학습할 때 위 정보를 이어서 학습할 수 있게 됨!
model.load_state_dict(checkpoint[&#39;model_state_dict&#39;])
epoch = checkpoint[&#39;epoch&#39;]
optimizer.load_state_dict(checkpoint[&#39;optimizer_state_dict&#39;])
loss = checkpoint[&#39;loss&#39;]</code></pre>
<p><br><br></p>
<h1 id="데이터-다루기">데이터 다루기</h1>
<h2 id="pytorch에서-dataloader-만들기">PyTorch에서 Dataloader 만들기</h2>
<p><code>torch.utils.data.DataLoader</code>를 사용하면 된다!</p>
<pre><code class="language-python">batch_size = 32

# 기존에 우리가 했던 방법
train_loader = torch.utils.data.DataLoader(
    datasets.MNIST(&#39;dataset/&#39;,
                   train=True, download=True, # 로컬 환경에 데이터가 없다면 다운로드하라
                   transform=transforms.Compose([ # 가져올 때 미리 아래와 같이 전처리해서 가져오겠다
                       transforms.ToTensor(),
                       transforms.Normalize(mean=(0.5,), std=(0.5,)) # mean이 0.5이고 std가 0.5가 되는 분포가 되도록 정규화시켜라.
                                                                    # 튜플 형태로 넣어주어야함
                   ])),
    batch_size=batch_size,
    shuffle=True)</code></pre>
<br>

<h2 id="로컬-데이터를-torchutilsdatadataset으로-만들기">로컬 데이터를 <code>torch.utils.data.Dataset</code>으로 만들기</h2>
<p><code>ImageFolder</code>를 이용하면 되는데 tf의 <code>flow_from_directory</code>와 같은 제약 조건이 있다.
<img src="https://velog.velcdn.com/images/castle_mi/post/ca88ba85-c667-422b-8a6a-e204babfb15f/image.png" alt=""></p>
<p>해당 조건이 만족한다면 아래와 같은 방법으로 데이터셋을 만들 수 있다.</p>
<pre><code class="language-python">device = torch.device(&#39;cuda&#39; if torch.cuda.is_available() else &#39;cpu&#39;)

train_dir = &#39;../../datasets/mnist_png/training/&#39;
test_dir = &#39;../../datasets/mnist_png/testing&#39;

train_dataset = datasets.ImageFolder(
    root = train_dir,
    transform=transforms.Compose([
        transforms.ToTensor(),
        transforms.Normalize(0.5, 0.5)
    ])
)

test_dataset = datasets.ImageFolder(
    root = test_dir,
    transform=transforms.Compose([
        transforms.ToTensor(),
        transforms.Normalize(0.5, 0.5)
    ])
)
# x, y = next(iter(train_dataset))
# x.shape, y # (torch.Size([3, 28, 28]), 0)

train_loader = torch.utils.data.DataLoader(
    train_dataset,
    batch_size = 32,
    shuffle = True
)

test_loader = torch.utils.data.DataLoader(
    test_dataset,
    batch_size = 32,
    shuffle = True
)

x, y = next(iter(train_loader))
x.shape, y.shape # 배치 사이즈만큼 뽑힘 # (torch.Size([32, 3, 28, 28]), torch.Size([32]))</code></pre>
<br>

<h2 id="custom-dataset-다루기">Custom dataset 다루기</h2>
<p><code>ImageFolder</code>는 제약사항이 많으므로 직접 <code>torch.utils.data.dataset.Dataset</code> 상속받아 데이터셋을 구현해보자.</p>
<p>지금 현재 파일 이름에 클래스의 이름이 있고 이를 <code>label.txt</code>의 인덱스와 매치시켜 원핫인코딩된 타겟 데이터를 얻어낼 수 있는 구조이다.</p>
<pre><code class="language-python"># custom
class Dataset(torch.utils.data.Dataset):
    def __init__(self,data_paths, transform = None):
        super(Dataset).__init__()
        self.data_paths = data_paths
        self.transform = transform

    def __len__(self):
        return len(self.data_paths)

    def __getitem__(self, idx):
        # 이미지, 레이블 추출
        path = self.data_paths[idx] # 파일 경로
        image = Image.open(path) # return 할 이미지
        label_name = path.split(&#39;.png&#39;)[0].split(&#39;_&#39;)[-1].strip() # 클래스 라벨 이름
        label = label_list.index(label_name) # 원핫인코딩역할을 하는 타겟 데이터

        if self.transform:
            image = self.transform(image)
        return image, label

device = torch.device(&#39;cuda&#39; if torch.cuda.is_available() else &#39;cpu&#39;)

batch_size = 32

train_loader = torch.utils.data.DataLoader(
    Dataset(train_paths, transform=transforms.ToTensor()),
    batch_size = batch_size, shuffle = True
)

test_loader = torch.utils.data.DataLoader(
    Dataset(test_paths, transform=transforms.ToTensor()),
    batch_size = batch_size
)

x, y = next(iter(train_loader))
x.shape, y.shape # (torch.Size([32, 3, 32, 32]), torch.Size([32]))</code></pre>
<p><br><br></p>
<h2 id="transform">Transform</h2>
<pre><code class="language-python">from torchvision import transforms

image = Image.open(&#39;/content/drive/MyDrive/제로베이스스쿨 공부/Deep Learning/data/sample.png&#39;)</code></pre>
<ul>
<li><code>transforms.Resize([512, 512])(image)</code> : 해당 픽셀로 resize</li>
<li><code>transforms.RandomCrop(size = (512, 512))(image)</code> : 해당 픽셀만큼 랜덤으로 crop</li>
<li><code>transforms.ColorJitter(brightness = 1)(image)</code> : 랜덤으로 밝기 조절</li>
<li><code>transforms.Grayscale()(image)</code> : grayscale 적용</li>
<li><code>transforms.Pad(padding = (20 , 10))(image)</code> : padding 적용</li>
<li><code>transforms.RandomAffine(degrees = 90)(image)</code> : 랜덤하게 회전</li>
<li><code>transforms.RandomHorizontalFlip(p = 1.)(image)</code> : 반전</li>
</ul>
<br>

<p>위 과정을 한 번에 모두 수행하고 싶다면</p>
<pre><code class="language-python"># 한 번에
transform_list = [
    transforms.RandomAffine(degrees = 90),
    transforms.Pad((20,20))
]</code></pre>
<p>확률에 의해 적용시키고 싶다면</p>
<pre><code class="language-python"># 확률에 의해 적용
transforms.RandomApply(transform_list, p = 0.5)(image)</code></pre>
<p>랜덤하게 한 가지만 적용시키고 싶다면</p>
<pre><code class="language-python"># transform_list 중 랜덤하게 한 개만 적용
transforms.RandomChoice(transform_list)(image)</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[PyTorch] Tensor 연산 / 자동 미분 / Linear Regression /  DL Flow]]></title>
            <link>https://velog.io/@castle_mi/PyTorch-Tensor-%EC%97%B0%EC%82%B0-%EC%9E%90%EB%8F%99-%EB%AF%B8%EB%B6%84-Linear-Regression-DL-Flow</link>
            <guid>https://velog.io/@castle_mi/PyTorch-Tensor-%EC%97%B0%EC%82%B0-%EC%9E%90%EB%8F%99-%EB%AF%B8%EB%B6%84-Linear-Regression-DL-Flow</guid>
            <pubDate>Sun, 05 Nov 2023 15:28:55 GMT</pubDate>
            <description><![CDATA[<p>✍🏻 5일 공부 이야기.</p>
<p><a href="https://github.com/castlemi99/ZeroBaseDataSchool/tree/main/PyTorch"><img src="https://velog.velcdn.com/images/castle_mi/post/1495ed76-320e-4059-8f10-5f4a6e941d51/image.png" alt=""></a>
오늘 공부한 실습 코드는 위 깃허브에 올려두었습니다 :) 사진 클릭시 해당 링크로 이동해요 ! </p>
<br>

<p>앞서 배웠던 <code>TensorFlow</code> 와 같은 흐름으로 <code>PyTorch</code> 도 배워보자. 둘은 같이 개발되고 있기 때문에 기능이 거의 다 비슷하다.</p>
<p><code>PyTorch</code> 도</p>
<ol>
<li>Tensor 다루기</li>
<li>연산 정의 및 Modeling</li>
<li>최적화</li>
<li>데이터 다루기</li>
</ol>
<p>4 파트만 익히면 된다!</p>
<br>

<h1 id="tensor-다루기">Tensor 다루기</h1>
<p>앞서 Variable과 Constant가 달리 있었던 <code>TensorFlow</code>와 달리 <code>PyTorch</code>에서는 이를 하나로 모두 <code>Tensor</code>로 본다.</p>
<ul>
<li><p><code>torch.tensor(변환시킬 데이터)</code> , <code>torch.as_tensor(변환시킬 데이터)</code> : 기존의 데이터를 Tensor로 변환</p>
</li>
<li><p><code>torch.tensor(변환시킬 데이터)</code> , <code>torch.as_tensor(변환시킬 데이터)</code> , <code>torch.from_numpy(변환시킬 데이터)</code> : numpy 데이터를 Tensor로 변환</p>
</li>
<li><p><code>변환시킬 데이터.numpy()</code> : Tensor -&gt; numpy</p>
</li>
<li><p><code>.shape</code> , <code>.size</code> : Tensor의 속성값 확인</p>
</li>
<li><p>데이터 타입 선언</p>
<ul>
<li><p>생성시 선언</p>
<pre><code class="language-python"># 생성시 선언 dtype=torch.float32
torch.randint(10, size=(5,), dtype=torch.float32)</code></pre>
</li>
<li><p>이미 생성된 변수에 선언</p>
<pre><code class="language-python">a = torch.randint(10, size=(5,))
print(a.dtype)

# 텐서이름.type(원하는 데이터타입)
print(a.type(torch.float32))
print(a.dtype)

# inplace 명령어가 아니므로 갱신시켜주어야함
a = a.type(torch.float64)
print(a.dtype)</code></pre>
</li>
</ul>
</li>
<li><p><code>torch.cuda.is_available()</code> : GPU 사용 </p>
</li>
<li><p>GPU에서 사용하기 위해 cuda에서 사용하는 데이터 타입으로 바꿔줘야함</p>
<ul>
<li><p>방법 1. 생성 시 device 설정</p>
<pre><code class="language-python"># 방법 1. device 설정하기.
x = torch.ones(2, 2, device=&#39;cuda&#39;)

# 여러개의 GPU 중에 하나의 GPU에 할당하고 싶을 때
# 번호는 nvidia-smi 명령을 Shell에 입력해서 찾을 수 있음 
x = torch.ones(2, 2, device=&#39;cuda:0&#39;)

# device 객체를 입력하는게 기본
x = torch.ones(2, 2, device=torch.device(&#39;cuda&#39;))</code></pre>
</li>
<li><p>방법 2. <code>tensor_var.cuda()</code></p>
<pre><code class="language-python"># 방법 2. .cuda()
a = torch.rand(10)
print(a)

a = a.cuda()
print(a)</code></pre>
</li>
<li><p><strong>방법 3. <code>tensor_var.to(device)</code></strong></p>
<pre><code class="language-python"># 방법 3. .to(device) : 대부분 많이 사용
a = torch.rand(2)
print(a)

a = a.to(&quot;cuda&quot;) # a.to(torch.device(&quot;cuda&quot;))
print(a)</code></pre>
</li>
</ul>
</li>
</ul>
<h2 id="연산">연산</h2>
<p>대부분의 연산은 <code>TensorFlow</code>와 동일하다. 몇 가지 특이 케이스만 정리해보았다.</p>
<ul>
<li><p>tensorflow의 <code>axis</code> -&gt; pytorch <code>dim</code></p>
</li>
<li><p>크기와 차원 변경</p>
<ul>
<li><code>torch.reshape</code></li>
<li><code>텐서명.view</code> : expand_dims 같은 함수 대신 이걸로 사용</li>
<li><code>torch.transpose</code></li>
<li><code>torch.sqeeze</code> / <code>torch.unsqueeze</code></li>
<li><code>텐서이름.view_as(다른 텐서이름)</code> / <code>텐서이름.reshape_as(다른 텐서이름)</code> : 이전 shape에 맞춰서 두번째 텐서의 shape이 변경됨</li>
</ul>
</li>
<li><p>대부분 함수 끝에 _ 를 붙이면 inplace 명령이 됨</p>
</li>
<li><p>아래와 같은 오류가 발생한다면 <code>텐서이름.contiguous()</code> 호출하고 다시 실행시키기
<img src="https://velog.velcdn.com/images/castle_mi/post/4fd7b5d7-78ac-4bda-a75c-d76df1883175/image.png" alt=""></p>
</li>
</ul>
<p><br><br></p>
<h1 id="연산-정의-및-modeling">연산 정의 및 Modeling</h1>
<h2 id="자동-미분">자동 미분</h2>
<p><code>autograd</code>는 PyTorch에서 핵심적인 기능을 담당하는 하부 패키지이다. </p>
<p>autograd는 텐서의 연산에 대해 자동으로 미분값을 구해주는 기능을 한다.</p>
<ul>
<li>텐서 자료를 생성할 때, <code>requires_grad</code>인수를 <code>True</code>로 설정하거나 </li>
<li><code>.requires_grad_(True)</code>를 실행하면</li>
</ul>
<p>** 📌 그 텐서에 행해지는 모든 연산에 대한 미분값을 계산**한다. </p>
<pre><code class="language-python"># requires_grad=True : 미분값을 트래킹할 변수임을 선언
x = torch.rand(2, 2, requires_grad=True)

y = torch.sum(x * 3)
print(y, y.grad_fn) # grad_fn라는 속성값이 생기게 되면서 미분 값을 트래킹할 수 있게됨 

y.backward() # 선언 후(x의 미분값이 자동으로 갱신됨)
x.grad # 미분을 구하고자하는 변수.grad 를 하면 미분값이 구해짐</code></pre>
<br>

<p><strong>📌 상황에 따라 특정 연산에서는 미분값을 계산하고 싶지 않은 경우</strong></p>
<p>계산을 멈추고 싶으면 <code>.detach()</code>함수나 with을 이용해 <code>torch.no_grad()</code>를  이용하면 된다.</p>
<pre><code class="language-python">x_d = x.detach() # 미분값 트래킹 x
torch.sigmoid(x_d)
print(x_d.grad) # None

with torch.no_grad():
    x_d2 = torch.sigmoid(x)
    print(x_d2.grad) # None</code></pre>
<p><br><br></p>
<h2 id="linear-regression">Linear Regression</h2>
<p><code>Boston</code> 데이터를 이용해 Linear Regression 을 PyTorch로 구현해보자.</p>
<h3 id="역행렬-존재시-가중치-구하는-방법">역행렬 존재시 가중치 구하는 방법</h3>
<p><a href="https://datascienceschool.net/03%20machine%20learning/04.02%20%EC%84%A0%ED%98%95%ED%9A%8C%EA%B7%80%EB%B6%84%EC%84%9D%EC%9D%98%20%EA%B8%B0%EC%B4%88.html?highlight=intercept"><img src="https://velog.velcdn.com/images/castle_mi/post/71ea59d1-0751-49dd-a074-fadad938c877/image.png" alt=""></a></p>
<p>위 수식을 이용해보자.</p>
<pre><code class="language-python">x = torch.tensor(df.values)
y = torch.tensor(y.values).view(-1, 1)

XT = torch.transpose(x, 0, 1) 
# 역행렬이 존재시, 가중치 벡터 구하는 수식
w = torch.matmul(torch.matmul(torch.inverse(torch.matmul(XT, x)), XT), y)
y_pred = torch.matmul(x, w)

print(&quot;예측한 집값 :&quot;, y_pred[19], &quot;실제 집값 :&quot;, y[19])</code></pre>
<blockquote>
<p> 💻 출력</p>
</blockquote>
<p>예측한 집값 : tensor([18.4061], dtype=torch.float64) 실제 집값 : tensor([18.2000], dtype=torch.float64)</p>
<br>

<h3 id="gradient-descent-방법">Gradient Descent 방법</h3>
<p><strong>📌 loss 추출하기</strong></p>
<pre><code class="language-python"># 가중치 생성
# const 열을 추가했기 때문에 이론 상으로 b는 필요없지만
# 그냥 일반적인 경우를 설명하기 위해 맞춰준 것
w = torch.rand((14, 1), dtype=torch.float64, requires_grad=True)
b = torch.rand((1, 1),  dtype=torch.float64, requires_grad=True)

# linear regression
z = x.mm(w) + b
loss = torch.mean((z - y)**2) 

loss.backward()

# print(loss)
# tensor(303557.1678, dtype=torch.float64, grad_fn=&lt;MeanBackward0&gt;)

# loss 숫자만 추출하는 방법
# 303557.16782532405
print(loss.detach().numpy())  # 방법 1

with torch.no_grad(): # 방법 2
    print(loss.numpy())
# 이렇게 로그를 추출할 때는 숫자만 추출하도록 설정해줌
# 방법 3
loss.item()</code></pre>
<br>

<p><strong>📌 추출한 gradient로 가중치 업데이트하기</strong></p>
<pre><code class="language-python">lr = 3e-7
for epoch in range(100):
    z = x.mm(w) + b
    loss = torch.mean((y-z)**2)

    loss.backward() # 미분값 계산

    w.data -= w.grad * lr # 업데이트
    b.data -= b.grad * lr

    # 위 코드와 동일
    ## 미분값 계산
    #grads = torch.autograd.grad(loss, [w, b])
    #
    #w.data -= grads[0] * lr # 업데이트
    #b.data -= grads[1] * lr

    print(&#39;{} - loss : {}&#39;.format(epoch, loss.item()))

    # 초기화
    # 누적값을 초기화해주어야함
    w.grad.zero_()
    b.grad.zero_()</code></pre>
<br>

<h3 id="optimizer-방법">optimizer 방법</h3>
<pre><code class="language-python">opt = torch.optim.SGD([w, b], lr=lr)

for epoch in range(100):    
    z = (x.mm(w) + b)
    loss = torch.mean((z - y)**2)  

    loss.backward()
    opt.step()
    print(&quot;{:3} - loss : {}&quot;.format(epoch, loss.item()), end=&quot;\r&quot;)

    opt.zero_grad()

y_pred = x.mm(w) + b
print(&quot;예측한 집값 :&quot;, y_pred[19].item(), &quot;실제 집값 :&quot;, y[19].item())</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>예측한 집값 : 22.069598463384597 실제 집값 : 18.2</p>
<p><br><br></p>
<h2 id="deep-learning-flow">Deep Learning Flow</h2>
<p>*<em>Data Load 및 전처리 -&gt; 데이터 확인 -&gt; 모델 정의 -&gt; 모델 학습 -&gt; 평가 *</em></p>
<h3 id="data-load-및-전처리">Data Load 및 전처리</h3>
<pre><code class="language-python">batch_size = 32

# 데이터 부르기
train_loader = torch.utils.data.DataLoader(
    datasets.MNIST(&#39;dataset/&#39;,
                   train=True, download=True, # 로컬 환경에 데이터가 없다면 다운로드하라
                   transform=transforms.Compose([ # 가져올 때 미리 아래와 같이 전처리해서 가져오겠다
                       transforms.ToTensor(),
                       transforms.Normalize(mean=(0.5,), std=(0.5,)) # mean이 0.5이고 std가 0.5가 되는 분포가 되도록 정규화시켜라.
                                                                    # 튜플 형태로 넣어주어야함
                   ])),
    batch_size=batch_size,
    shuffle=True)

test_loader = torch.utils.data.DataLoader(
    datasets.MNIST(&#39;dataset/&#39;,
                   train=False,
                   transform=transforms.Compose([ 
                       transforms.ToTensor(),
                       transforms.Normalize(mean=(0.5,), std=(0.5,))                                                     
                   ])),
    batch_size=batch_size,
    shuffle=False)</code></pre>
<br>

<h3 id="데이터-확인">데이터 확인</h3>
<pre><code class="language-python">images, labels = next(iter(train_loader))
images.shape, images.dtype # (torch.Size([32, 1, 28, 28]), torch.float32)</code></pre>
<blockquote>
<ul>
<li>TF - (batch, height, width, channel)</li>
</ul>
</blockquote>
<ul>
<li>PyTorch - (batch, channel, height, width)</li>
</ul>
<p>PyTorch와 TF는 이미지를 표현하는데에 있어서 channel의 위치 차이가 있으니 명심하자!!</p>
<br>

<h3 id="모델-정의">모델 정의</h3>
<p>주로 <code>nn.Module</code>을 상속 받아서 모델을 정의한다.</p>
<pre><code class="language-python">from torch import nn # 학습할 파라미터가 있는 것들 # conv2d
import torch.nn.functional as F # 학습할 파라미터가 없는 것들 # maxpool, relu

class Net(nn.Module):
    def __init__(self): # forward에서 사용되는 레이어, 파라미터 정의
        super(Net, self).__init__()
                              # tf에서는 output만 넣어주었다면
                              # pytorch에서는 input과 output을 같이 정의해줌
                              # input, output
        self.conv1 = nn.Conv2d(1, 20, 5, 1)
        self.conv2 = nn.Conv2d(20, 50, 5, 1)
        self.fc1 = nn.Linear(4*4*50, 500)
        self.fc2 = nn.Linear(500, 10)

    def forward(self, x): # 실제 연산
        x = F.relu(self.conv1(x))
        x = F.max_pool2d(x, 2, 2)
        x = F.relu(self.conv2(x))
        x = F.max_pool2d(x, 2, 2)
        x = x.view(-1, 4*4*50)
        x = F.relu(self.fc1(x))
        x = self.fc2(x)
        return F.log_softmax(x, dim=1)

device = torch.device(&#39;cuda&#39; if torch.cuda.is_available() else &#39;cpu&#39;)
model = Net().to(device) # 모델을 device로 넘겨줌</code></pre>
<p>model을 이쁘게 정리해서 출력해주는 <code>torchsummary</code> 는 이후 모델링 파트에서 정리할 예정!</p>
<br>

<h3 id="학습-및-평가">학습 및 평가</h3>
<pre><code class="language-python">&#39;&#39;&#39;
- epoch
  - batch
    - model
    - loss
    - grad
    - model update
&#39;&#39;&#39;

import torch.optim as optim

opt = optim.SGD(model.parameters(), lr=0.003)

for epoch in range(1):
  # 학습
  model.train()
  for batch_idx, (data, target) in enumerate(train_loader):
    data, target = data.to(device) , target.to(device) # 데이터 device로 보내기

    output = model(data)
    loss = F.nll_loss(output, target)
          # 지금 원핫인코딩이 안 되어있는 상태이다.
          # tf 에서는 SparseCategoricalCrossentropy loss를 사용해
          # 원핫 인코딩이 안 되어있는 상태에서도 사용 가능했는데
          # 이를 torch에서 구현하려면 log softmax와 nll_loss를 조합하면 됨.

    loss.backward()

    opt.step() # 업데이트

    print(&#39;batch {} loss : {}&#39;.format(batch_idx, loss.item()))

    opt.zero_grad() # 초기화

  # 평가
  model.eval()

  test_loss = 0

  with torch.no_grad():
    for data, target in test_loader:
      output = model(data)
      test_loss += F.nll_loss(output, target).item()
  # 한 epoch 마다 배치 데이터의 평균 loss 값
  test_loss /= (len(test_loader.dataset) // 32)

  print(&#39;Epoch {} test loss : {}&#39;.format(epoch, test_loss))</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[텐서플로] 모델링 / 학습 / Tensorboard / 모델 저장 및 불러오기 / 데이터 다루기 ]]></title>
            <link>https://velog.io/@castle_mi/%ED%85%90%EC%84%9C%ED%94%8C%EB%A1%9C-s2w7w4ma</link>
            <guid>https://velog.io/@castle_mi/%ED%85%90%EC%84%9C%ED%94%8C%EB%A1%9C-s2w7w4ma</guid>
            <pubDate>Sun, 05 Nov 2023 14:35:35 GMT</pubDate>
            <description><![CDATA[<p>✍🏻 3일 공부 이야기.</p>
<p><a href="https://github.com/castlemi99/ZeroBaseDataSchool/tree/main/TensorFlow"><img src="https://velog.velcdn.com/images/castle_mi/post/759a1405-d0cd-452f-9673-548dd463a652/image.png" alt=""></a>
오늘 학습한 실습 코드는 위 깃허브 사진 클릭 시 이동합니다 :)</p>
<p><br><br></p>
<p>👀 이전 시간 리마인드 
<img src="https://velog.velcdn.com/images/castle_mi/post/fe324581-e7d9-479e-a5ba-40156fd44fe6/image.png" alt="">
딥러닝 프로젝트를 하기 위해 위 4가지는 꼭 할 줄 알아야한다고 했었구
<img src="https://velog.velcdn.com/images/castle_mi/post/92d2b5dc-5bfd-43b0-97ef-c7397e925bc4/image.png" alt="">
프로젝트는 크게 총 4단계로 구성되어있다고 했었다!</p>
<p>프로젝트의 흐름을 다루는 방법을 이어서 살펴보자 :)</p>
<h1 id="modeling">Modeling</h1>
<blockquote>
<p><strong>📌 텐서플로에서 모델링 하는 방법</strong></p>
</blockquote>
<ol>
<li>Sequential</li>
<li>Functional API model</li>
<li>Sub class model(자유도가 가장 높으나 코드가 길어짐)</li>
</ol>
<p>1번부터 다시 살펴보자.</p>
<h2 id="sequential">Sequential</h2>
<h3 id="vggnet">VGGNet</h3>
<blockquote>
<ul>
<li><code>tf.keras.layers.Conv2D</code></li>
</ul>
</blockquote>
<ul>
<li><code>tf.keras.layers.Activation</code></li>
<li><code>tf.keras.layers.MaxPool2D</code></li>
<li><code>tf.keras.layers.Flatten</code></li>
<li><code>tf.keras.layers.Dense</code></li>
</ul>
<p>이번 시간에는 CNN의 VGGNet 모델을 구현해보자.</p>
<p>CNN은 원래 이미지 데이터 중에서도 컬러 이미지 데이터를 입력으로 받는 모델이었기 때문에 입력 데이터의 shape이 (num_data, 28, 28, 3)과 같은 형태였다. </p>
<p>우리가 앞서 만들었던 <code>Dataloader</code>클래스는 (num_data, 28, 28)의 입력을 받았기 때문에 <code>채널수</code>를 입력받을 수 있도록 차원을 늘려주는 작업이 필요하다.</p>
<pre><code class="language-python">class DataLoader():
    def __init__(self):
        # data load
        (self.train_x, self.train_y), \
            (self.test_x, self.test_y) = tf.keras.datasets.mnist.load_data()

    def scale(self, x):
        return (x / 255.0).astype(np.float32)

    def preprocess_dataset(self, dataset):

        (feature, target) = dataset

        # scaling #
        scaled_x = np.array([self.scale(x) for x in feature])

        ###### Add channel axis ######
        expanded_x = scaled_x[:, :, :, np.newaxis] # np.newaxis : 가짜 차원 하나 추가

        # label encoding #
        ohe_y = np.array([tf.keras.utils.to_categorical(
            y, num_classes=10) for y in target])

        return expanded_x, ohe_y

    def get_train_dataset(self):
        return self.preprocess_dataset((self.train_x, self.train_y))

    def get_test_dataset(self):
        return self.preprocess_dataset((self.test_x, self.test_y))</code></pre>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/9ea8a925-c691-434f-84bd-247f90859ada/image.png" alt=""></p>
<p>그리고 VGGNet을 구현해보자.</p>
<ul>
<li><p><code>tf.keras.layers.Conv2D</code>
<img src="https://velog.velcdn.com/images/castle_mi/post/b4728411-a8ad-48b5-985d-d46d3a3f0d85/image.png" alt=""></p>
<ul>
<li>filters: layer에서 사용할 Filter(weights)의 갯수<ul>
<li>kernel_size: Filter(weights)의 사이즈  </li>
<li>strides: 몇 개의 pixel을 skip 하면서 훑어지나갈 것인지 (출력 피쳐맵의 사이즈에 영향을 줌)</li>
<li>padding: zero padding을 만들 것인지. VALID는 Padding이 없고, SAME은 Padding이 있음 (출력 피쳐맵의 사이즈에 영향을 줌)</li>
<li>activation: Activation Function을 지정</li>
</ul>
</li>
</ul>
</li>
<li><p><code>tf.keras.layers.MaxPool2D</code>
<img src="https://velog.velcdn.com/images/castle_mi/post/fa784140-0042-4eaa-a7a8-3178b1a483d2/image.png" alt=""></p>
<ul>
<li>pool_size: Pooling window 크기</li>
<li>strides: 몇 개의 pixel을 skip 하면서 훑어지나갈 것인지 </li>
<li>padding: zero padding을 만들 것인지</li>
</ul>
</li>
<li><p><code>tf.keras.layers.Flatten</code><br><img src="https://velog.velcdn.com/images/castle_mi/post/b6810153-1a6a-4c2d-a4bd-8f3f628cc496/image.png" alt=""></p>
<ul>
<li><p><code>tf.keras.layers.Dense</code>
<img src="https://velog.velcdn.com/images/castle_mi/post/66f5e054-569d-470b-920b-4dda8d3ea9c4/image.png" alt=""></p>
<ul>
<li>units : 노드 갯수</li>
<li>activation : 활성화 함수</li>
<li>use_bias : bias 를 사용 할 것인지</li>
<li>kernel_initializer : 최초 가중치를 어떻게 세팅 할 것인지</li>
<li>bias_initializer : 최초 bias를 어떻게 세팅 할 것인지</li>
</ul>
</li>
</ul>
</li>
</ul>
<br>

<p><strong>📌 모델 구성</strong></p>
<pre><code class="language-python">from tensorflow.keras.layers import Conv2D, MaxPool2D, Flatten, Dense

model = tf.keras.Sequential()
# 최초의 레이어는 Input의 shape을 명시해준다. (이 때 배치 axis는 무시한다.)
model.add(Conv2D(32, kernel_size=3, padding=&#39;same&#39;, activation=&#39;relu&#39;, input_shape=(28, 28, 1))) 
model.add(Conv2D(32, kernel_size=3, padding=&#39;same&#39;, activation=&#39;relu&#39;))
model.add(MaxPool2D())
model.add(Conv2D(64, kernel_size=3, padding=&#39;same&#39;, activation=&#39;relu&#39;)) 
model.add(Conv2D(64, kernel_size=3, padding=&#39;same&#39;, activation=&#39;relu&#39;))
model.add(MaxPool2D())
model.add(Flatten())
model.add(Dense(128, activation=&quot;relu&quot;))
model.add(Dense(64, activation=&quot;relu&quot;))
model.add(Dense(10, activation=&quot;softmax&quot;)) # output class 개수 10

model.summary()</code></pre>
<br>

<p><strong>📌 optimizer, loss 선언 및 컴파일</strong></p>
<pre><code class="language-python">learning_rate = 0.03
opt = tf.keras.optimizers.Adam(learning_rate)
loss = tf.keras.losses.categorical_crossentropy

model.compile(optimizer=opt, loss=loss, metrics=[&quot;accuracy&quot;])</code></pre>
<br>

<p><strong>📌 학습 및 평가</strong></p>
<pre><code class="language-python"># 학습
hist = model.fit(train_x, train_y,
                 epochs=2, batch_size=128,
                 validation_data=(test_x, test_y)) 
                # validation_data 를 지정해주면 .evaluate를 굳이 하지 않아도 매 epochs마다 저절로 해줌



# 평가
plt.figure(figsize=(10, 5))
plt.subplot(221)
plt.plot(hist.history[&#39;loss&#39;])
plt.title(&quot;loss&quot;)
plt.subplot(222)
plt.plot(hist.history[&#39;accuracy&#39;], &#39;b-&#39;)
plt.title(&quot;acc&quot;)
plt.subplot(223)
plt.plot(hist.history[&#39;val_loss&#39;])
plt.title(&quot;val_loss&quot;)
plt.subplot(224)
plt.plot(hist.history[&#39;val_accuracy&#39;], &#39;b-&#39;)
plt.title(&quot;val_accuracy&quot;)

plt.tight_layout()
plt.show()</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/8e31e634-5535-4816-bdfc-8985937489f6/image.png" alt=""></p>
<p><br><br></p>
<h2 id="functional-api-model">Functional API model</h2>
<p>앞서 만들었던 모델을 Functional API를 이용해 만든다면 아래와 같은 코드를 짤 수 있다.</p>
<pre><code class="language-python">from tensorflow.keras.layers import Input, Conv2D, MaxPool2D, Flatten, Dense

input_shape=(28, 28, 1)

inputs = Input(input_shape) # 입력을 받는 레이어가 첫번째 레이어

net = Conv2D(32, kernel_size=3, padding=&#39;same&#39;, activation=&#39;relu&#39;)(inputs) # 입력값을 inputs 변수로 넣어줌
net = Conv2D(32, kernel_size=3, padding=&#39;same&#39;, activation=&#39;relu&#39;)(net)
net = MaxPool2D()(net)

net = Conv2D(64, kernel_size=3, padding=&#39;same&#39;, activation=&#39;relu&#39;)(net)
net = Conv2D(64, kernel_size=3, padding=&#39;same&#39;, activation=&#39;relu&#39;)(net)
net = MaxPool2D()(net)
net = Flatten()(net)

net = Dense(128, activation=&quot;relu&quot;)(net)
net = Dense(64, activation=&quot;relu&quot;)(net)
net = Dense(10, activation=&quot;softmax&quot;)(net)

model = tf.keras.Model(inputs=inputs, outputs=net, name=&#39;VGG&#39;)
model.summary()</code></pre>
<p>입력 데이터를 받는 입력 레이어가 첫 번째로 오고 다른 레이어들은 동일하게 써주되, 마지막에 input 데이터로 이전 레이어를 명시해주면서 레이어를 쌓아간다.</p>
<p>그리고 마지막에 <code>tf.keras.Model</code>를 선언하여 모델을 만들 수 있는데 이게 왜 <code>Sequential</code>보다 유연하다는 것일까?</p>
<p><code>ResNet</code> 모델을 만들어보면서 &lt;유연하다&gt;의 의미를 한 번 살펴보자.</p>
<br>

<h3 id="resnet">ResNet</h3>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/7dbfe97f-6911-46bc-8707-f01e3c5b493f/image.png" alt="">
<code>ResNet</code>은 기본 아이디어가 Input 데이터와 Output 데이터를 합쳐가며 학습을 진행했더니 성능이 더 좋더라~! 이다.</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/ff855e54-05ef-4c84-9539-df4f3262281a/image.png" alt="">
이렇듯 <code>ResNet</code>의 핵심은 왼쪽 그림과 같은 Input을 넣으면 그냥 Output을 출력해주는 흐름이 아닌 오른쪽 그림과 같이 Input과 Output을 더해주어야하는 것이다.</p>
<p>이를 Functional API로 구현해보자.</p>
<br>

<p><strong>📌 Functional API로 구현한 ResNet</strong></p>
<pre><code class="language-python">from tensorflow.keras.layers import Input, Conv2D, MaxPool2D, Flatten, Dense, Add

## Functional API 를 이용해 ResNet 구현
def build_resnet(input_shape):
    inputs = Input(input_shape) # 입력 데이터를 받는 첫 번째 레이어 # (28, 28)

    net = Conv2D(32, kernel_size=3, strides=2,
                 padding=&#39;same&#39;, activation=&#39;relu&#39;)(inputs) # (14, 14)
    net = MaxPool2D()(net) # (7, 7)

    net1 = Conv2D(64, kernel_size=1, padding=&#39;same&#39;, activation=&#39;relu&#39;)(net)
    net2 = Conv2D(64, kernel_size=3, padding=&#39;same&#39;, activation=&#39;relu&#39;)(net1)
    net3 = Conv2D(64, kernel_size=1, padding=&#39;same&#39;, activation=&#39;relu&#39;)(net2)

    # net과 net3을 더해주어야하는데 filter 개수가 32와 64로 맞지 않는다
    # 이를 맞춰주기 위해 net1_1 생성
    net1_1 = Conv2D(64, kernel_size=1, padding=&#39;same&#39;)(net)
    net = Add()([net1_1, net3]) # input과 output을 더해준 것

    net1 = Conv2D(64, kernel_size=1, padding=&#39;same&#39;, activation=&#39;relu&#39;)(net)
    net2 = Conv2D(64, kernel_size=3, padding=&#39;same&#39;, activation=&#39;relu&#39;)(net1)
    net3 = Conv2D(64, kernel_size=1, padding=&#39;same&#39;, activation=&#39;relu&#39;)(net2)

    net = Add()([net, net3]) # input과 output을 더해준 것

    net = MaxPool2D()(net)

    net = Flatten()(net)
    net = Dense(10, activation=&quot;softmax&quot;)(net)

    model = tf.keras.Model(inputs=inputs, outputs=net, name=&#39;resnet&#39;)

    return model</code></pre>
<br>

<p>CIFAR 10 데이터로 학습해보자면
<img src="https://velog.velcdn.com/images/castle_mi/post/c2b04b8e-d92b-4086-9cb3-547709bf7da3/image.png" alt="">
해당 데이터의 input shape에 맞게 모델을 불러와주고 학습시키면 된다.</p>
<br>

<p><strong>📌 모델 선언</strong></p>
<pre><code class="language-python">model = build_resnet((32, 32, 3))

lr = 0.03
opt = tf.keras.optimizers.Adam(lr)
loss = tf.keras.losses.categorical_crossentropy

model.compile(optimizer=opt, loss=loss, metrics=[&quot;accuracy&quot;])</code></pre>
<br>

<p><strong>📌 학습 및 평가</strong></p>
<pre><code class="language-python"># 학습
hist = model.fit(train_x, train_y,
                 epochs=10, batch_size=128,
                 validation_data=(test_x, test_y))

# 평가
plt.figure(figsize=(10, 5))
plt.subplot(221)
plt.plot(hist.history[&#39;loss&#39;])
plt.title(&quot;loss&quot;)
plt.subplot(222)
plt.plot(hist.history[&#39;accuracy&#39;], &#39;b-&#39;)
plt.title(&quot;acc&quot;)
plt.subplot(223)
plt.plot(hist.history[&#39;val_loss&#39;])
plt.title(&quot;val_loss&quot;)
plt.subplot(224)
plt.plot(hist.history[&#39;val_accuracy&#39;], &#39;b-&#39;)
plt.title(&quot;val_accuracy&quot;)

plt.tight_layout()
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/6148116f-3a6b-42b1-ac4b-942afa082389/image.png" alt=""></p>
<p>모델 중간의 값을 활용할 수 있다는 점에서 <code>Sequential</code>보다 유연한 것 같다!</p>
<p><br><br></p>
<h2 id="sub-class-model">Sub class model</h2>
<h3 id="linear-regression">Linear Regression</h3>
<br>

<p><strong>📌 Linear Regression model</strong></p>
<pre><code class="language-python">class LinearRegression(tf.keras.layers.Layer):
    def __init__(self, units):
        super(LinearRegression, self).__init__()
        self.units = units # output 개수

    def build(self, input_shape): # 변수를 받는 오버라이딩 
        self.w = self.add_weight(
            shape=(input_shape[-1], self.units),
            initializer=&quot;random_normal&quot;, # 어느 분포에서 random하게 추출할 것인지 설정
            trainable=True,
        )
        self.b = tf.Variable(0.0) # 위와 같이 만들어도 되고 이 문장과 같이 만들어도 됨

    def call(self, inputs): # 출력을 해줄 값을 지정하는 오버라이딩
        return tf.matmul(inputs, self.w) + self.b</code></pre>
<p><code>tf.keras.layers.Layer</code>를 상속받아 사용하는 것이기 때문에 &lt;상속&gt;과 &lt;오버라이딩&gt;에 익숙해질 필요가 있어보였다.</p>
<p><a href="https://www.tensorflow.org/api_docs/python/tf/keras/layers/Layer#methods"><img src="https://velog.velcdn.com/images/castle_mi/post/7903472d-f545-4f4d-9fec-786bc71bca17/image.png" alt=""></a></p>
<p>위 사진은 <code>tf.keras.layers.Layer</code> 메서드들을 볼 수 있는 공식 사이트이다. 필요한 메서드가 있다면 오버라이딩해서 사용하면 될 듯 하다.</p>
<p>가상의 데이터를 만들어 위 내용을 실습해보자.</p>
<p><strong>📌 데이터 준비</strong></p>
<pre><code class="language-python"># 정답
W_true = np.array([[3., 2., 4., 1.]]).reshape((4, 1))
B_true = np.array([1.]) 

X = tf.random.normal((500, 4))
noise = tf.random.normal((500, 1))

y = X @ W_true + B_true + noise</code></pre>
<p><strong>📌 학습</strong> </p>
<pre><code class="language-python">opt = tf.keras.optimizers.SGD(learning_rate=0.03)


linear_layer = LinearRegression(1)

for epoch in range(100):
    with tf.GradientTape() as tape:
        y_hat = linear_layer(X)
        loss = tf.reduce_mean(tf.square((y - y_hat)))

    grads = tape.gradient(loss, linear_layer.trainable_weights)
    # assign 대신 업데이트하는 방식
    opt.apply_gradients(zip(grads, linear_layer.trainable_weights))

    if epoch % 10 == 0:
        print(&quot;epoch : {} loss : {}&quot;.format(epoch, loss.numpy()))</code></pre>
<p>기존에는 <code>.assign</code>을 이용하여 가중치를 업데이트해주었다면 <code>.apply_gradients</code>를 통해 업데이트 할 수 있다는 것을 배울 수 있었다.</p>
<p>그렇다면 이번에는 앞서 구현했던 <code>ResNet</code> 모델을 Sub class로 구현해보자.</p>
<h3 id="resnet-1">ResNet</h3>
<p>동일하게 반복되는 구간이 있었는데(conv2D 3번 - input과 output 더하는 코드) 이런 부분을 <code>Residual Block</code>이라 한다. 이 부분을 하나의 레이어로 만들어 은닉화 하는 과정을 거친 후 모델을 구현해보자.</p>
<br>

<p><strong>📌 Residual Block</strong></p>
<pre><code class="language-python">class ResidualBlock(tf.keras.layers.Layer):
    def __init__(self, filters=32, filter_match=False):
        super(ResidualBlock, self).__init__()

        # 필요한 레이어들 받아오기
        self.conv1 = Conv2D(filters, kernel_size=1, padding=&#39;same&#39;, activation=&#39;relu&#39;)
        self.conv2 = Conv2D(filters, kernel_size=3, padding=&#39;same&#39;, activation=&#39;relu&#39;)
        self.conv3 = Conv2D(filters, kernel_size=1, padding=&#39;same&#39;, activation=&#39;relu&#39;)
        self.add = Add()

        self.filters = filters
        self.filter_match = filter_match

        # 첫번째 step에서 filter 개수가 맞지 않아
        # 한 번 Conv2D를 다시 64로 생성하여 input과 output을 add해준 적이 있다.
        # 그 부분을 조건문으로 처리하면 아래와 같다.
        if filter_match:
            self.conv_ext = Conv2D(filters, kernel_size=1, padding=&#39;same&#39;)

    def call(self, inputs):
        net1 = self.conv1(inputs)
        net2 = self.conv2(net1)
        net3 = self.conv3(net2)

        if self.filter_match:
            res = self.add([self.conv_ext(inputs), net3])
        else: 
            res = self.add([inputs, net3])
        return res 

</code></pre>
<p>앞서 반복된 부분을 처리해주고, 
첫 번째 input과 output을 더할 때 dim이 맞지 않아 conv2D를 한 번 더 해주고 더한 코드 부분은 조건문을 통해 처리하도록 해주었다.</p>
<p>이를 이용하여 <code>ResNet</code> 을 구성하면 아래와 같다.</p>
<br>

<p><strong>📌 ResNet using Sub Class</strong></p>
<pre><code class="language-python">class ResNet(tf.keras.Model):

    def __init__(self, num_classes):
        super(ResNet, self).__init__()

        self.conv1 = Conv2D(32, kernel_size=3, strides=2, padding=&#39;same&#39;, activation=&#39;relu&#39;)
        self.maxp1 =  MaxPool2D()
        self.block_1 = ResidualBlock(64, True)
        self.block_2 = ResidualBlock(64)
        self.maxp2 =  MaxPool2D()
        self.flat = Flatten()
        self.dense = Dense(num_classes)

    def call(self, inputs):
        x = self.conv1(inputs)
        x = self.maxp1(x)
        x = self.block_1(x)
        x = self.block_2(x)
        x = self.maxp2(x)
        x = self.flat(x)
        return self.dense(x)</code></pre>
<p>아마 비교해보면 코드도 훨씬 간단해보일 것이다.</p>
<p>나는 아직 상속과 오버라이딩에 익숙하지 않아서 이해하는데에 시간이 조금 걸렸지만.. ㅠ 익숙해지기만 한다면 더할 나위없이 모델을 구성하는 데에 좋은 방법이 될 것 같다!!</p>
<br>

<p>👀 <code>tf.keras.Model</code> 공식 사이트
<a href="https://www.tensorflow.org/api_docs/python/tf/keras/Model#methods"><img src="https://velog.velcdn.com/images/castle_mi/post/aa99aeb8-9d0b-4662-bfcc-ab2722925261/image.png" alt=""></a></p>
<p>그리고 Functional API와 Sub Class로 만든 ResNet을 비교하여 보고 싶어서 정리해본 자료도 첨부해두겠다.
<img src="https://velog.velcdn.com/images/castle_mi/post/a136a4c5-1604-43e7-aae7-7d8e3a75327b/image.png" alt=""></p>
<p>마지막으로 만든 ResNet 모델을 학습시키기 위해 CIFAR 10 데이터를 이용했으며 결과는 아래와 같다.</p>
<pre><code class="language-python"># train_y.shape (50000, 10)
model = ResNet(num_classes=10)

learning_rate = 0.03
opt = tf.keras.optimizers.Adam(learning_rate)
loss = tf.keras.losses.categorical_crossentropy

model.compile(optimizer=opt, loss=loss, metrics=[&quot;accuracy&quot;])

hist = model.fit(train_x, train_y,
                 epochs=2, batch_size=128,
                 validation_data=(test_x, test_y))</code></pre>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/04da02f2-b559-4e38-94ae-b3b81b7454b2/image.png" alt=""></p>
<p><br><br><br></p>
<h1 id="model-학습">Model 학습</h1>
<h2 id="fit">Fit</h2>
<p><code>model.compile() -&gt; model.fit()</code>을 통해 모델을 학습시킨다는 사실은 알고 있을 것이다.</p>
<p>몇 가지만 체크하고 넘어가자 :)</p>
<blockquote>
<p><strong>📌 <code>compile()</code>의 입력값</strong></p>
</blockquote>
<ul>
<li><strong>optimizer</strong>=&#39;rmsprop&#39; : Optimizer <ul>
<li><strong>loss</strong>=None : Loss function</li>
<li><strong>metrics</strong>=None : Metrics</li>
<li><strong>loss_weights</strong>=None : loss가 여러 개인 경우 각 로스마다 다르게 중요도를 설정 할 수 있다. </li>
</ul>
</li>
</ul>
<p>이때 loss와 metrics를 지정할 수 있는 방법이 여러 가지가 있다.</p>
<p>그리고 여러 개의 loss와 metrics를 사용하고 싶다면 리스트 형태로 묶어주면 된다. </p>
<h3 id="loss-지정하는-방법">loss 지정하는 방법</h3>
<p><strong>1. <code>tf.keras.losses</code> 로 지정</strong>
    우리가 그동안 많이 썼던 방법 !</p>
<pre><code class="language-python">   learning_rate = 0.03
   opt = tf.keras.optimizers.Adam(learning_rate)
   loss = tf.keras.losses.categorical_crossentropy

   model.compile(optimizer=opt, loss=loss, metrics=[&quot;accuracy&quot;])</code></pre>
<p><strong>2. 사용자 정의 함수로 지정</strong></p>
<pre><code class="language-python">    # 사용자 정의 loss : 입력으로 정답값, 예측값의 순서로 꼭 받아야함
    def custom_loss(y_true, y_pred):
        return tf.reduce_mean(tf.square(y_true - y_pred))

    model.compile(optimizer=opt, loss=custom_loss, metrics=[&quot;accuracy&quot;])</code></pre>
<p>여러 개로 지정하고 싶다면 아래와 같이 리스트의 형태로 묶어주면 되고 <code>loss_weights</code>를 이용해 각각의 loss에 대해 중요도까지 설정해주면 각 loss에 weights를 곱해서 더한 값을 최종 loss로 사용하게 된다.</p>
<pre><code class="language-python">#여러 개의 Loss : 리스트 형태로 입력
model.compile(optimizer=opt, loss=[loss, custom_loss], metrics=[&quot;accuracy&quot;])

#여러 개의 Loss + loss weights
# 각각의 loss에 대한 중요도 조절
# 각 loss에 wieghts를 곱해서 더한 값을 최종 loss로 사용
model.compile(optimizer=opt, loss=[loss, custom_loss], loss_weights=[0.7, 0.3], metrics=[&quot;accuracy&quot;])</code></pre>
<br>

<p><strong>3. 텍스트로 지정</strong></p>
<pre><code class="language-python">loss = &quot;categorical_crossentropy&quot; # 이렇게 텍스트로 가능한 함수도 있음.

model.compile(optimizer=opt, loss=loss, metrics=[&quot;accuracy&quot;])</code></pre>
<p><br><br></p>
<h3 id="metrics-지정하는-방법">metrics 지정하는 방법</h3>
<p><strong>1. <code>tf.keras.metrics</code> 로 지정</strong></p>
<pre><code class="language-python">  # mertircs에도 여러 개를 넣을 수 있음
  acc = tf.keras.metrics.Accuracy()
  auc = tf.keras.metrics.AUC()

  model.compile(optimizer=opt, loss=loss, metrics=[acc, auc])</code></pre>
<p><strong>2. 사용자 정의 함수로 지정</strong></p>
<pre><code class="language-python">  def custom_metric(y_true, y_pred):

      true = tf.argmax(y_true, axis=-1)
      pred = tf.argmax(y_pred, axis=-1)

      return tf.reduce_sum(tf.cast(tf.equal(true, pred), tf.int32))

  model.compile(optimizer=opt, loss=loss, metrics=[custom_metric])</code></pre>
<h3 id="fit-1">Fit</h3>
<p><code>compile</code>을 완료했다면 이제 학습시킬 차례!</p>
<p><code>Fit</code>의 입력값으로는 대강 아래와 같은 것들이 있다.</p>
<blockquote>
<ul>
<li>x=None </li>
</ul>
</blockquote>
<ul>
<li>y=None</li>
<li>batch_size=None</li>
<li>epochs=1</li>
<li>verbose=&#39;auto&#39; : 학습과정 출력문의 모드</li>
<li>callbacks=None : Callback 함수</li>
<li>validation_split=0.0 : 입력데이터의 일정 부분을 Validation 용 데이터로 사용함</li>
<li>validation_data=None : Validation 용 데이터 </li>
<li>shuffle=True : 입력값을 Epoch 마다 섞는다. </li>
<li>class_weight=None : 클래스 별로 다른 중요도를 설정한다. </li>
<li>...</li>
</ul>
<pre><code class="language-python">hist = model.fit(train_x, 
                 train_y,
                 epochs=1, 
                 batch_size=128, 
                 validation_split=0.3,
                 verbose=1
                )</code></pre>
<p>그동안 했던 방식과 같이 위와 같은 형태로 fit을 해주면 된다.</p>
<p>해당 코드가 돌아가는 동안 우리는 아무 작업도 할 수 없는 상태가 되는데, 함수가 돌아가는 와중에도 특정한 주기로 원하는 코드를 실행시킬 수 있게 해주는 것이 <code>callback</code> 함수이다.</p>
<h3 id="callback">callback</h3>
<p>입력값으로 epoch과 lr를 받는  <code>tf.keras.callbacks.LearningRateScheduler</code> 를 이용해 작성되는 callback은 학습이 진행됨에 따라 lr를 점점 줄여서 안정적으로 수렴을 하도록 도움을 준다던가, earlystoping을 한다던가, history를 남기는 등 다양한 기능이 있다.</p>
<p>아래는 epoch이 10 이상이 되면 lr을 줄여 안정적으로 수렴할 수 있도록 해주는 callback 함수이다.</p>
<pre><code class="language-python"># epoch이 10을 초과할 때마다 lr을 감소시켜 안정적으로 수렴하도록 도움을 줌
def scheduler(epoch, lr):
    if epoch &gt; 10:
        return lr * (0.9**(epoch - 10))
    else: 
        return lr

lr_scheduler = tf.keras.callbacks.LearningRateScheduler(scheduler)

hist = model.fit(train_x, 
                 train_y,
                 epochs=20, 
                 batch_size=128, 
                 validation_split=0.3,
                 verbose=1,
                 callbacks=[lr_scheduler],
                )</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/0e94c562-7d96-476a-b5ab-8b4001e223eb/image.png" alt=""></p>
<p>마지막 열에 <code>loss</code>도 같이 출력되는 것을 볼 수 있다!</p>
<br>



<h2 id="logicfit-과정을-직접-구현해보자">Logic(Fit 과정을 직접 구현해보자)</h2>
<p>앞서 model의 학습을 <code>fit</code>을 통해 해결했지만 자유도가 높은 모델을 구현하고자 한다면 학습하는 과정을 직접 코드로 구현해야하는 경우도 생긴다.</p>
<p><code>fit</code>을 하드코딩해보자.</p>
<pre><code class="language-python">&#39;&#39;&#39;
for e in epochs:
    for batch_x, batch_y in dataset:
        pred = model(batch_x)
        loss_fn(batch_y, pred)

        gradients
        weight_update

        print
&#39;&#39;&#39;</code></pre>
<p>대략적으로 우리는 위와 같은 흐름으로 학습이 진행되어야하는 것을 알고 있다.</p>
<p>lr, opt, loss, metrics를 선언해주고
학습하는 과정을 함수로 만든 다음,
각 epoch의 배치 데이터마다 코드가 돌아가는 과정을 구현해보자.</p>
<br>

<p><strong>📌 lr, opt, loss, metrics를 선언</strong></p>
<pre><code class="language-python">learning_rate = 0.03
# optimizer
opt = tf.keras.optimizers.Adam(learning_rate) # 클래스
# loss 
loss_fn = tf.keras.losses.categorical_crossentropy # 함수로 만든 것
# metrics
train_loss = tf.keras.metrics.Mean(name=&#39;train_loss&#39;) # 클래스 객체로 만든 것
train_accuracy = tf.keras.metrics.CategoricalAccuracy(name=&#39;train_accuracy&#39;)</code></pre>
<p>클래스로 선언한 것들은 각 메서드를 사용할 수 있다는 말이기도 하다 :)</p>
<br>

<p><strong>📌 학습 과정(pred, loss gradient update까지)</strong></p>
<pre><code class="language-python"># 학습 과정(pred, loss gradient update까지)
@tf.function
def train_step(x, y) :
    with tf.GradientTape() as tape:
        pred = model(x) # 예측값
        loss = loss_fn(y, pred) # loss 계산

    gradients = tape.gradient(loss, model.trainable_variables) # gradient 계산
    opt.apply_gradients(zip(gradients, model.trainable_variables)) # 업데이트

    # metric
    train_loss(y)
    train_accuracy(y, pred)</code></pre>
<p><code>@tf.function</code>을 하는 이유는 아래와 같다.</p>
<blockquote>
<p><code>@tf.function</code>으로 함수 위에 붙여주면 GPU로 학습시켰을 때 학습 속도가 향상되는 효과를 볼 수 있다.
그냥 함수로 정의만 해두면 매 배치 데이터를 돌 때마다 계속 함수를 새로 불러오는 형식으로 진행되는데 <code>@tf.function</code>를 해두면 한 번 불러오고 저장해두므로 불러 쓰기가 더 쉬워진다.</p>
</blockquote>
<br>

<p>*<em>📌 매 epoch에 대해 배치 데이터마다 학습하는 과정 *</em></p>
<pre><code class="language-python">batch_size = 64

for epoch in range(1): # epoch을 도는데 

    for i in range(train_x.shape[0] // batch_size): # 배치 데이터마다 계산
        idx = i * batch_size
        x, y = train_x[idx:idx+batch_size], train_y[idx:idx+batch_size] # 배치 데이터 추출
        train_step(x, y)  # 학습 과정
        print(&quot;\r {} / {}&quot;.format(i, train_x.shape[0] // batch_size), end=&#39;\r&#39;) # 진행률 출력

    # 한 epoch에 대한 loss와 acc 출력
    fmt = &#39;epoch {} loss: {}, accuracy: {}&#39;
    print(fmt.format(epoch+1, 
                          train_loss.result(), # .result() : 객체의 값을 반환해줌 
                          train_accuracy.result() * 100)
         )

    # 초기화
    # 매 epoch마다 새로운 metric 값을 계산해야지,
    # 만약 초기화를 해주지 않으면 이전 epoch의 metric 값이 계속 누적된 채로 계산됨
    train_loss.reset_states()
    train_accuracy.reset_states()</code></pre>
<p>이때 <code>.reset_states()</code>로 매 epoch 본연의 값을 가질 수 있도록 초기화해주는 것이 가장 중요하다!!!</p>
<p><br><br><br></p>
<h1 id="evaluation">Evaluation</h1>
<h2 id="tensorboard">Tensorboard</h2>
<p>보통 딥러닝 프로젝트를 하면 여러가지 모델을 동시에 돌려두고 기다려야하는 시간이 길기 때문에 로그만으로 트래킹하기 힘들다. 따라서 <code>Tensorboard</code>의 시각화 툴을 이용하여 모델의 진행 상황을 파악하는 데에 도움을 얻을 수 있다.</p>
<p><a href="https://www.tensorflow.org/tensorboard?hl=ko">https://www.tensorflow.org/tensorboard?hl=ko</a></p>
<p>TensorFlow에서 제공하는 시각화툴로 중간의 그래프나 여러가지 정보를 Web UI로 조회할 수 있는 툴인데, 
나는 무슨 이유 때문인지 VSCode에서도 그렇고 Colab에서도 링크가 연결이 되지 않은 이슈가 있었다 😅 </p>
<p>그래도 일단 정리해보는... </p>
<h3 id="방법-1-fit-함수를-이용한다면-callback-함수-이용하기">방법 1. fit 함수를 이용한다면 callback 함수 이용하기</h3>
<p><code>fit</code>을 할 때 <code>callbacks</code> 옵션을 사용할 수 있는데 이때 <code>TensorBoard</code>를 사용하는 방법이다.</p>
<pre><code class="language-python"># 로그끼리 섞이지 않도록 잘 정리하는 것이 중요
import datetime
cur_time = datetime.datetime.now().strftime(&#39;%Y%m%d-%H%M%S&#39;)
log_dir = &#39;logs/fit/&#39; + cur_time # &#39;logs/fit/20231104-233628&#39;

tb_callback = tf.keras.callbacks.TensorBoard(log_dir = log_dir)

model.fit(x=train_x, 
          y=train_y, 
          epochs=5,
          validation_data=(test_x, test_y),
          callbacks=[tb_callback]
         )</code></pre>
<p>로그끼리 섞이지 않도록 개인의 규칙을 따라 폴더 경로를 설정해주고 fit했다면</p>
<p><code>!tensorboard --logdir logs/fit --bind_all</code>을 통해 TensorBoard로 이동하는 링크가 하나 생길 것이다. 그 사이트에서 로그들의 결과를 확인할 수 있다.</p>
<p><br><br></p>
<h3 id="방법-2-tfsummary-사용하기">방법 2. <code>tf.summary</code> 사용하기</h3>
<pre><code class="language-python">current_time = datetime.datetime.now().strftime(&quot;%Y%m%d-%H%M%S&quot;)
train_log_dir = &#39;logs/gradient_tape/&#39; + current_time + &#39;/train&#39;
test_log_dir = &#39;logs/gradient_tape/&#39; + current_time + &#39;/test&#39;

train_summary_writer = tf.summary.create_file_writer(train_log_dir)
test_summary_writer = tf.summary.create_file_writer(test_log_dir)</code></pre>
<p>위와 같이 <code>tf.summary</code>를 이용해 파일을 생성한다는 선언을 해주고
학습을 해주면 된다.</p>
<pre><code class="language-python">batch_size = 64

num_of_batch_train = train_x.shape[0] // batch_size
num_of_batch_test = test_x.shape[0] // batch_size

for epoch in range(5):

    for i in range(num_of_batch_train):
        idx = i * batch_size
        x, y = train_x[idx:idx+batch_size], train_y[idx:idx+batch_size]
        train_step(x, y)
        print(&quot;\r Train : {} / {}&quot;.format(i, num_of_batch_train), end=&#39;\r&#39;)


    for i in range(num_of_batch_test):
        idx = i * batch_size
        x, y = test_x[idx:idx+batch_size], test_y[idx:idx+batch_size]
        test_step(x, y)
        print(&quot;\r Test : {} / {}&quot;.format(i, num_of_batch_test), end=&#39;\r&#39;)

    with train_summary_writer.as_default():
        tf.summary.scalar(&#39;loss&#39;, train_loss.result(), step=epoch)
        tf.summary.scalar(&#39;acc&#39;, train_accuracy.result(), step=epoch)

    with test_summary_writer.as_default():
        tf.summary.scalar(&#39;loss&#39;, test_loss.result(), step=epoch)
        tf.summary.scalar(&#39;acc&#39;, test_accuracy.result(), step=epoch)

    fmt = &#39;epoch {} loss: {}, accuracy: {}, test_loss: {}, test_acc: {}&#39;
    print(fmt.format(epoch+1, 
                          train_loss.result(),
                          train_accuracy.result(),
                          test_loss.result(),
                          test_accuracy.result()
                    )
         )

    train_loss.reset_states()
    test_loss.reset_states()
    train_accuracy.reset_states()
    test_accuracy.reset_states()</code></pre>
<p>이 또한 학습을 다 진행시킨 후 <code>!tensorboard --logdir logs/gradient_tape</code>을 실행시키면 Tensorboard로 이동할 수 있는 링크가 뜬다.</p>
<p>아래는 Tensorboard에 <strong>이미지 데이터를 기록하는 방법</strong>과 <strong>Confusion Matrix를 기록</strong>하는 방법이다.</p>
<p>📌 <strong>이미지 데이터를 기록</strong></p>
<pre><code class="language-python">logdir = &quot;logs/train_data/&quot; + datetime.datetime.now().strftime(&quot;%Y%m%d-%H%M%S&quot;)
file_writer = tf.summary.create_file_writer(logdir)

for i in np.random.randint(10000, size=10):    
    img = train_x[i:i+1]
    with file_writer.as_default():
        tf.summary.image(&quot;Training Sample data : {}&quot;.format(i), img, step=0)

!tensorboard --logdir logs/train_data</code></pre>
<br>

<p>📌 <strong>Confusion Matrix를 기록</strong></p>
<pre><code class="language-python">import io
from sklearn.metrics import confusion_matrix

def plot_to_image(figure):
    buf = io.BytesIO()
    plt.savefig(buf, format=&#39;png&#39;)
    plt.close(figure)
    buf.seek(0)
    image = tf.image.decode_png(buf.getvalue(), channels=4)
    image = tf.expand_dims(image, 0)
    return image

def plot_confusion_matrix(cm, class_names):

    figure = plt.figure(figsize=(8, 8))
    plt.imshow(cm)
    plt.title(&quot;Confusion matrix&quot;)
    plt.colorbar()
    tick_marks = np.arange(len(class_names))
    threshold = cm.max() / 2.
    for i in range(cm.shape[0]):
        for j in range(cm.shape[1]):
            color = &quot;white&quot; if cm[i, j] &gt; threshold else &quot;black&quot;
            plt.text(j, i, cm[i, j], horizontalalignment=&quot;center&quot;, color=color)

    plt.tight_layout()
    plt.ylabel(&#39;True label&#39;)
    plt.xlabel(&#39;Predicted label&#39;)
    return figure

logdir = &quot;logs/fit/cm/&quot; + datetime.datetime.now().strftime(&quot;%Y%m%d-%H%M%S&quot;)
file_writer_cm = tf.summary.create_file_writer(logdir)

test_images = test_x[:100]
test_labels = np.argmax(test_y[:100], axis=1)

def log_confusion_matrix(epoch, logs):
    test_pred_raw = model.predict(test_images)
    test_pred = np.argmax(test_pred_raw, axis=1)

    classes = np.arange(10)
    cm = confusion_matrix(test_labels, test_pred, labels=classes)

    figure = plot_confusion_matrix(cm, class_names=classes)
    cm_image = plot_to_image(figure)

    with file_writer_cm.as_default():
        tf.summary.image(&quot;Confusion Matrix&quot;, cm_image, step=epoch)

# callback 정의
cm_callback = tf.keras.callbacks.LambdaCallback(on_epoch_end=log_confusion_matrix)

# 학습
model.fit(x=train_x, 
          y=train_y,
          epochs=5,
          batch_size=32,
          validation_data=(test_x, test_y),
          callbacks=[tensorboard_callback, cm_callback])

# 텐서보드로 확인
!tensorboard --logdir logs/fit</code></pre>
<p>윈도우에서는 /가 아닌 \로 인식해야해서 안된다는 사람, 파일을 읽고 쓰는 권한이 없어서 안된다는 사람 등등.. 구글링해보니깐 상황이 여러가지이던데 나는 왜 안되는지 모르겠다 ㅜㅠ 403에러가 떴는데,, 나중에 함 알아봐야지...!!</p>
<p><br><br></p>
<h1 id="model-save-and-load">Model Save and Load</h1>
<p>모델을 저장하고 불러올 수 있는 방법으로 간단히 코드만 소개하겠다.</p>
<h2 id="save-함수로-저장">save 함수로 저장</h2>
<pre><code class="language-python"># 저장
model.save(&quot;checkpoints/sample/model.h5&quot;) # 원하는 경로 입력

# 불러오기
model_loaded = tf.keras.models.load_model(&quot;checkpoints/sample/model.h5&quot;)

# 확인
model_loaded.summary()</code></pre>
<br>

<h2 id="save_weights-함수로-저장">save_weights 함수로 저장</h2>
<p>weights만 저장 하므로, 저장공간이 절약된다.</p>
<pre><code class="language-python"># 저장
model.save_weights(&quot;checkpoints/sample/model.h5&quot;)

# 새로운 모델에 가중치 입히기
new_model = build_resnet((32, 32, 3))
# 불러오기
new_model.load_weights(&quot;checkpoints/sample/model.h5&quot;)

# 확인
print(model.predict(test_x[:1]))
print(new_model.predict(test_x[:1]))</code></pre>
<p>기존의 모델과 동일하게 예측값을 보여주는 것을 확인할 수 있다.</p>
<br>

<h2 id="callbacks-함수로-저장">Callbacks 함수로 저장</h2>
<pre><code class="language-python"># 저장
save_path = &#39;checkpoints/{epoch:02d}-{val_loss:.2f}.h5&#39;
checkpoint = tf.keras.callbacks.ModelCheckpoint(save_path, 
                                                monitor=&#39;val_accuracy&#39;, 
                                                save_best_only=True)

model.fit(x=train_x, 
          y=train_y, 
          epochs=1,
          validation_data=(test_x, test_y), 
          callbacks=[checkpoint])</code></pre>
<br>

<h2 id="pb-형식으로-저장">pb 형식으로 저장</h2>
<pre><code class="language-python"># 저장
save_path = &#39;checkpoints/{epoch:02d}-{val_loss:.2f}&#39;
checkpoint = tf.keras.callbacks.ModelCheckpoint(save_path, 
                                                monitor=&#39;val_accuracy&#39;, 
                                                save_best_only=True)

model.fit(x=train_x, 
          y=train_y, 
          epochs=1,
          validation_data=(test_x, test_y), 
          callbacks=[checkpoint])

# 불러오기
model = tf.saved_model.load(&quot;checkpoints/01-2.32&quot;)</code></pre>
<p><br><br><br></p>
<h1 id="데이터-다루기">데이터 다루기</h1>
<p>드디어 텐서플로의 마지막 ! 데이터를 다루는 방법이다.
이 파트에서는 데이터를 어떻게 읽어들이는지에 대해 정리해보겠다.</p>
<h2 id="데이터-읽기">데이터 읽기</h2>
<p>지금은 로컬 환경에서 데이터를 읽는 작업이다.</p>
<pre><code class="language-python"># 특정 반복되는 형태의 파일을 쉽게 부를 수 있음
glob(&quot;../../datasets/cifar/train/*.png&quot;)</code></pre>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/fa413aa1-421c-4623-bc9b-7c97c42dbf14/image.png" alt=""></p>
<p>우리는 <code>glob</code>을 이용해 특정 반복되는 형태의 파일을 쉽게 읽을 수 있다.</p>
<p>위 데이터는 <code>CIFAR</code> 데이터인데 데이터가 엄청 많은 경우 일일이 읽어들인다면 메모리가 터질 것이다.</p>
<p>이때 <code>tf.data.API</code>를 이용하면 된다.</p>
<h3 id="tfdataapi">tf.data.API</h3>
<p>미리 이미지 데이터를 모두 불러오는게 아니라 그 때 그 때 처리를 하는 방식으로 진행되어 속도가 더 빠르다.</p>
<pre><code class="language-python">train_img = glob(&quot;../../datasets/cifar/train/*.png&quot;) # 읽어들일 파일 경로

# train_img를 하나하나 받아옴
# dataset = tf.data.Dataset.from_tensor_slices(train_img)


# tf.data.API
AUTOTUNE = tf.data.experimental.AUTOTUNE
                # .map 이후의 코드를 실행시킴
dataset = dataset.map(read_img, num_parallel_calls=AUTOTUNE)
# num_parallel_calls은 병렬처리 수준을 정하는 것
# AUTOTUNE으로 해두면 자동으로 정해준다. 

# 배치 처리
dataset = dataset.batch(32)
dataset = dataset.prefetch(AUTOTUNE) 
        # 앞 코드의 32배치를 읽어들이는 와중에 병렬적으로 그 다음 배치를 읽어들이고 있음
        # 스칼라 값을 넣거나 , AUTOTUNE으로 설정시 자동으로 그 값을 설정해줌

# next(iter(dataset))

dataset = dataset.shuffle(buffer_size=10) # 내부적으로 얼마만큼 섞을 것인가

# 여러번 epoch을 돌 때
dataset = dataset.repeat() # 입력값으로 숫자를 잘 넣지는 않음
# 아무것도 설정하지 않은채로 이후 for문의 코드에 epoch 숫자를 정해줌</code></pre>
<p>대부분 딥러닝에서의 전처리의 기본 세트이다.</p>
<pre><code class="language-python"># 대부분 딥러닝에서의 전처리의 기본 세트
AUTOTUNE = tf.data.experimental.AUTOTUNE
dataset = dataset.map(read_img, num_parallel_calls=AUTOTUNE)
dataset = dataset.batch(32)
dataset = dataset.prefetch(AUTOTUNE) 
dataset = dataset.shuffle(buffer_size=10) 
dataset = dataset.repeat() </code></pre>
<p>이미지를 읽을 때 경로에서 Label이 있었다. 
이미지를 넘겨줄 때 Label까지 같이 넘겨주는 것이 좋을 것이다!</p>
<br>

<p><strong>📌 Label 확인하기</strong></p>
<pre><code class="language-python">label_names = tf.io.read_file(&quot;../../datasets/cifar/labels.txt&quot;).numpy().decode(&#39;ascii&#39;).strip().split(&quot;\n&quot;)

np.array(&#39;frog&#39; == np.array(label_names)) # 클래스에 해당하는 값이 True가 됨
# 하지만 학습에 사용되는 것은 원핫인코딩된 값이므로 변환 필요

np.array(&#39;frog&#39; == np.array(label_names), dtype = np.float32) # 우리가 원하는 값!</code></pre>
<br>

<p><strong>📌 전체 데이터셋에 대하여</strong></p>
<pre><code class="language-python"># 전체 데이터셋에 대하여
label_txt = tf.io.read_file(&quot;../../datasets/cifar/labels.txt&quot;)
label_names = np.array(label_txt.numpy().decode(&#39;ascii&#39;).strip().split(&quot;\n&quot;))

def parse_label(path):
    name = path.split(&quot;/&quot;)[-1].split(&quot;.&quot;)[0].split(&quot;_&quot;)[-1]
    return np.array(name == label_names, dtype=np.float32)

train_y = np.array([parse_label(y) for y in train_img])

# image, Label 넘겨주기
def read_data(path, label):
    img = read_img(path)
    return img, label

dataset = tf.data.Dataset.from_tensor_slices((train_img, train_y))

# 대부분 딥러닝에서의 전처리의 기본 세트
AUTOTUNE = tf.data.experimental.AUTOTUNE
dataset = dataset.map(read_data, num_parallel_calls=AUTOTUNE)
dataset = dataset.batch(32)
dataset = dataset.prefetch(AUTOTUNE) 
dataset = dataset.shuffle(buffer_size=10) 
dataset = dataset.repeat()</code></pre>
<p>하지만 위 코드에서 <code>read_data</code> 함수에서 label을 입력값으로 받지 않고 return에서 바로 label을 넘겨주는 것이 코드가 더 효율적일 것이다.</p>
<p><code>.map</code> 함수 뒤에는 tensor 연산이 와야하므로 이를 주의하며 label을 return에서 넘겨주는 것을 map 함수로 처리해보면 아래와 같다.</p>
<br>

<p><strong>📌 조금 더 효율적인 코드</strong></p>
<pre><code class="language-python">def get_label(path):
    f_name = tf.strings.split(path, &#39;_&#39;)[-1] # 파일 이름
    lbl_name = tf.strings.regex_replace(f_name, &#39;.png&#39;, &#39;&#39;) # 파일 이름에서 클래스 이름 추출

    # 클래스 이름과 일치하는 원핫인코딩
    return tf.cast(lbl_name == label_names, tf.float32)


def load_image_label(path):
    gfile = tf.io.read_file(path)
    image = tf.io.decode_image(gfile)
    label = get_label(path)

    return image, label

dataset = tf.data.Dataset.from_tensor_slices(train_img)

# 대부분 딥러닝에서의 전처리의 기본 세트
AUTOTUNE = tf.data.experimental.AUTOTUNE
dataset = dataset.map(load_image_label, num_parallel_calls=AUTOTUNE)
dataset = dataset.batch(32)
dataset = dataset.prefetch(AUTOTUNE) 
dataset = dataset.shuffle(buffer_size=10) 
dataset = dataset.repeat() </code></pre>
<p><br><br></p>
<h3 id="imagedatagenerator">ImageDataGenerator</h3>
<p>(굳이 로컬 환경이 아니더라도) 데이터를 불러오는 동시에 여러가지 전처리를 쉽게 구현할 수 있는 기능으로 <code>tf.data.API</code> 보다 더 편리한 기능을 소개해보겠다.</p>
<pre><code class="language-python">datagen = ImageDataGenerator(
    # 아래의 작업을 랜덤하게 적용 또는 적용 X
    rotation_range=20, # 20도 각도 내에서 회전
    width_shift_range=0.2, # 가로로 이동
    height_shift_range=0.2, # 세로로 이동
    horizontal_flip=True) # 가로축 반전</code></pre>
<p>이게 끝이다!!ㅋㅋㅋㅋ</p>
<br>

<h4 id="flow">flow</h4>
<ul>
<li>데이터를 모두 메모리에 불러두고 사용할 때 유용</li>
</ul>
<p><code>train_x</code>와 <code>train_y</code>를 불러온 후</p>
<pre><code class="language-python">result = next(iter(datagen.flow((train_x, train_y))))

x, y = result
x.shape, y.shape # ((32, 32, 32, 3), (32, 10))</code></pre>
<p>위 코드를 실행시켜주면 랜덤하게 이미지가 바뀌는 것을 볼 수 있을 것이다.</p>
<br>

<h4 id="flow_from_directory">flow_from_directory</h4>
<ul>
<li>데이터가 너무 커서 하나씩 불러와야 할 때 유용</li>
</ul>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/358a4c91-abed-48d3-8274-6af0dcf00a8a/image.png" alt=""></th>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/2633904a-6c1d-496a-bb52-9f4fa9cf551e/image.png" alt=""></th>
</tr>
</thead>
</table>
<p>하지만 이 모듈을 사용하려면 조건이 있다.
위 사진과 같이 </p>
<ol>
<li>분류나 클래스가 숫자로 구분되어있는 문제에 사용 가능</li>
<li>클래스가 각 폴더 별로 나누어져 있고</li>
<li>각 폴더 아래 클래스에 해당하는 파일들이 존재하는 데이터</li>
</ol>
<p>위 3가지 조건에 해당해야 사용 가능하다.</p>
<p>지금 MNIST 데이터가 그렇게 되어있으니 한 번 이용해보자.</p>
<pre><code class="language-python">input_shape = (28, 28, 1)
batch_size = 32

gen =  datagen.flow_from_directory(
    train_dir,
    target_size = input_shape[:2] , # 입력데이터를 어떤 사이즈로 줄여줄지
    batch_size = batch_size,
    color_mode = &#39;grayscale&#39; # 채널 설정
)
# Found 60000 images belonging to 10 classes.

x , y = next(iter(gen))
x.shape, y.shape # ((32, 28, 28, 1), (32, 10))</code></pre>
<br>

<h4 id="flow_from_dataframe">flow_from_DataFrame</h4>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/de8028a4-824e-4a33-a47f-dfc7f6a8f68c/image.png" alt="">
위와 같이 경로와 타겟이 데이터프레임으로 만들어져있는 경우 사용할 수 있는 모듈이다.</p>
<p>위 데이터프레임은 직접 만들 수도 있으므로 <code>flow_from_directory</code>보단 자유로우며 메모리도 더 적게 사용한다.</p>
<pre><code class="language-python">gen = datagen.flow_from_dataframe(
    train_data,
    x_col=&quot;path&quot;,
    y_col=&quot;class_name&quot;,
    target_size=(32, 32), # input_size
    color_mode=&quot;rgb&quot;, # 채널 수
    class_model=&quot;categorical&quot;, 
    batch_size=32
)
# Found 50000 validated image filenames belonging to 10 classes.

x, y = next(iter(gen))
x.shape, y.shape # ((32, 32, 32, 3), (32, 10))

# 모델 생성 후 
# 학습은 model.fit(gen) 으로 가능</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[텐서플로] Tensor 연산 / 자동 미분 / Linear Regression / Perceptron / DL Flow]]></title>
            <link>https://velog.io/@castle_mi/%ED%85%90%EC%84%9C%ED%94%8C%EB%A1%9C-1</link>
            <guid>https://velog.io/@castle_mi/%ED%85%90%EC%84%9C%ED%94%8C%EB%A1%9C-1</guid>
            <pubDate>Sun, 05 Nov 2023 13:15:13 GMT</pubDate>
            <description><![CDATA[<p>✍🏻 3일 공부 이야기.</p>
<p><a href="https://github.com/castlemi99/ZeroBaseDataSchool/tree/main/TensorFlow"><img src="https://velog.velcdn.com/images/castle_mi/post/8b101c6b-5028-4921-8314-c8991fb6ed9f/image.png" alt=""></a></p>
<p>오늘 공부한 실습코드는 위 깃허브에 올려두었습니다 :) 사진 클릭시 깃허브로 이동해요 ! </p>
<br>



<h1 id="텐서플로">텐서플로</h1>
<h2 id="딥러닝-프레임워크">딥러닝 프레임워크</h2>
<p><em>🤔 딥러닝 프레임워크를 왜 공부해야할까?</em></p>
<p>먼저 딥러닝 알고리즘이 기존의 머신러닝 알고리즘과 다른 점이 있기 때문이다. 다른 점은 바로 <strong>&lt;연산량&gt;</strong></p>
<p>입력받는 픽셀의 크기와 노드의 수가 증가함에 따라 연산량은 어마무시하게 커져버린다.</p>
<p>딥러닝의 성능이 안 나오는데에는 학습시키는 데이터가 부족하다는 이유가 있을 것이다. 
어마어마한 연산량을 뚫고 파라미터를 최적화시키려면 데이터가 어느정도 있어야하기 때문이다.
또, 텍스트 데이터같은 경우는 이미지 데이터와 달리 수학적으로 표현하기 힘들다는 특징이 있다. 이런 수학적인 모델링이 힘든 것을 수학적으로 표현하려고 하니 연산량이 어마무시할 수 밖에 없을 것이다.</p>
<p>그렇다면 이 많은 연산량을 어떻게 처리할 수 있을까?</p>
<ul>
<li><p>분산 처리, 병렬 처리, 동시 처리에서 답을 찾았고
이를 도와주는 것이 바로 <strong>&lt;딥러닝 프레임워크&gt;</strong>이다.</p>
</li>
<li><p>그리고 분산 처리에 용이한 GPU를 이용하면 딥러닝을 그래프 형태로 만들어 많은 연산량을 커버하기 좋겠다는 생각으로 딥러닝 알고리즘은 발전해왔다고 한다.</p>
</li>
</ul>
<p>딥러닝 프레임워크에는 아래와 같이 많은 종류가 있지만, 
<img src="https://velog.velcdn.com/images/castle_mi/post/97439178-1d85-487a-a6af-d71a844ab9fd/image.png" alt="">
우리가 딥러닝 프레임워크를 왜 배워야하는지에 대해 알았으니 중요한 4가지만 할 줄 알면 하나의 딥러닝 프레임워크는 익혔다고 말할 수 있을 것이다.</p>
<blockquote>
<p><strong>1. Tensor를 생성하고 다루기(Tensor를 원하는 방향대로 다룰 수 있는가)</strong>
    <img src="https://velog.velcdn.com/images/castle_mi/post/68c31980-d2b7-4ad1-8c27-7978aa187bf3/image.png" alt=""></p>
</blockquote>
<blockquote>
<p><strong>2. 연산 정의하기(모델을 어떻게 연결할 것인가)</strong>
    <img src="https://velog.velcdn.com/images/castle_mi/post/e25cf1d9-b6ad-4bf9-bc5f-a250d57b1b12/image.png" alt=""></p>
</blockquote>
<blockquote>
<p><strong>3. 최적화(딥러닝 프레임워크는 미분을 어떻게 지원하는가)</strong>
    <img src="https://velog.velcdn.com/images/castle_mi/post/453ee433-f827-465f-9742-4e51a5480807/image.png" alt=""></p>
</blockquote>
<blockquote>
<p><strong>4. 데이터 다루기(딥러닝 프레임워크마다 입력 데이터를 GPU에 보내는 명령어가 다른데, 그것을 알고 있는가)</strong>
    <img src="https://velog.velcdn.com/images/castle_mi/post/e837318d-f35c-4353-b798-d909e2c15187/image.png" alt=""></p>
</blockquote>
<p>+) 모델 파일 저장, 학습 과정 모니터링 등등 추가 요소가 있긴 함 😚😚 </p>
<p><br><br></p>
<h2 id="tensorflow-vs-pytorch">TensorFlow vs PyTorch</h2>
<p><code>TensorFlow</code> 와 <code>PyTorch</code>를 사용하는데에 있어서 어떤 차이점이 있는지 살펴보자.</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/6e009d1e-5303-44c4-b5bb-4107af6980d2/image.png" alt=""></p>
<p>최근들어 많은 논문들이 <code>TensorFlow</code> 보다는 <code>PyTorch</code>를 사용하고 있는 것으로 보여졌다. </p>
<p>그렇다고 하더라도 구글의 연구나, Reinforcement Learning 분야, 상품화하기에는 여전히 <code>TensorFlow</code>를 많이 사용하고 있다. </p>
<p>우리는 둘 다 배워보쟈 :) </p>
<p><br><br></p>
<h1 id="1-tensor-다루기">1. Tensor 다루기</h1>
<p><code>import tensorflow as tf</code></p>
<blockquote>
<p>**[이번 챕터에서 기억해야할 것] **</p>
</blockquote>
<p>📌 Tensor를 다룰 땐 <code>shape</code>과 <code>dtype</code> 꼭 항상 체크하기
📌 Random seed 관리하기</p>
<h2 id="01-constants상수">01. Constants(상수)</h2>
<ul>
<li><code>tf.constant()</code> : 기존의 데이터 타입을 Tensor로 변환시켜줌
  <img src="https://velog.velcdn.com/images/castle_mi/post/97b4a2e4-4f3f-46b9-a0b3-d3698c93db31/image.png" alt=""></li>
</ul>
<ul>
<li><code>.numpy()</code> : 데이터타입을 numpy로 변환시켜줌
  <img src="https://velog.velcdn.com/images/castle_mi/post/bc4e575d-31e0-4f77-ada0-50ef9628e8ea/image.png" alt=""></li>
</ul>
<br>

<h3 id="tensor의-shape과-dtype확인">Tensor의 shape과 dtype확인</h3>
<p>Tensor를 다룰 때에는 제일 에러가 많이 나고, 제일 헷갈리는 이유가 <code>shape</code>과 <code>dtype</code> 때문이다.</p>
<p><strong>🌟 따라서 Tensor를 다룬다면 <code>shape</code>과 <code>dtype</code> 꼭 항상 체크해주는 습관을 기르자.</strong></p>
<ul>
<li><code>.shape</code></li>
<li><code>.dtype</code></li>
</ul>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/86c37230-d4cb-4140-84ab-cbf8899ab9f5/image.png" alt=""></p>
<p>굳이 명령어가 아니더라도 변수를 출력할 때 shape과 dtype을 함께 보여준다.</p>
<p>연산이 되지 않는 이유에는 크게 3가지가 있다. 따라서 연산을 하기 전 각각을 확인하고 연산을 하는 습관을 꼭 길러두자.</p>
<ol>
<li>Dim이 맞지 않아 연산 불가</li>
<li>Rank 수가 맞지 않아 연산 불가 -&gt; <code>.ndim</code></li>
<li>Dtype이 맞지 않아 연산 불가</li>
</ol>
<br>

<p>3번의 경우가 발생했다고 하자. </p>
<p>데이터 타입이 다른걸 확인했다면 데이터 타입을 맞춰주고 다시 연산을 진행시켜주면 된다.</p>
<p>3-1. 상수 선언 시, 데이터 타입을 미리 지정해주는 방법
3-2. <code>tf.cast</code>를 이용하여 이후에 데이터 타입을 지정해주는 방법
<img src="https://velog.velcdn.com/images/castle_mi/post/87f7de69-1bd7-4124-8991-9eb477ab9fcb/image.png" alt=""></p>
<p>나머지 1, 2번의 경우는 이후 03_Tensor 연산 파트에서 알아보자.</p>
<br>

<h3 id="특정-값의-tensor-생성">특정 값의 Tensor 생성</h3>
<ul>
<li><code>tf.ones</code> : 1이 들어간 Tensor 생성</li>
<li><code>tf.zeros</code> : 0이 들어간 Tensor 생성</li>
<li><code>tf.range</code> : 설정한 숫자들의 Tensor 생성</li>
</ul>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/15f5eb6e-5d6a-4407-b252-7676c30316da/image.png" alt=""></p>
<br>

<h3 id="random-value-생성">Random Value 생성</h3>
<p>Noise를 재현한다거나, Test를 할 때 무작위의 값을 생성하기 위해 난수를 많이 생성한다.</p>
<p><code>tf.random</code>을 통해 난수를 생성할 수 있으며 대표적으로 <code>tf.random.normal</code> , <code>tf.random.uniform</code>을 많이 사용하나, 아주 많은 분포가 있으니 아래 사이트를 참고하길 바란다.</p>
<p><a href="https://www.tensorflow.org/api_docs/python/tf/random">👀 <code>tf.random</code> 공식 사이트</a></p>
<br>

<p>난수를 생성하는 것은 좋으나, 매번 난수가 바뀌어버리면 자신이 했던 작업이 동일하게 복구 또는 재현이 되질 않는다.</p>
<p><strong>🌟 따라서 우리는 Random seed를 관리하는 습관도 기를 필요가 있다.</strong></p>
<ul>
<li><code>tf.random.set_seed(원하는 숫자)</code> </li>
</ul>
<p>원하는 숫자를 넣은 위 코드를 난수를 생성할 때마다 계속 출력하여 난수를 지정할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/5b1745a7-d456-42b1-8c4c-303d3c3055ce/image.png" alt=""></p>
<br>
<br>

<h2 id="02_variable변수">02_Variable(변수)</h2>
<p>변수는 일정 메모리 공간을 만들어두고 해당 공간에 값을 넣어주어 사용하는 것이다. </p>
<p><code>tf.Variable</code>을 통해 변수를 생성할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/eae5dfaf-c28a-4fe8-9d0a-4e944bddf8fe/image.png" alt=""></p>
<p>constant와 같이 <code>.shape</code>, <code>.dtype</code>, <code>.numpy</code> 등 기본 속성값들을 사용할 수도 있다.</p>
<p>또한 기존에 변수를 만들어 두었다 하더라도 텐서를 재할당할 수도 있다.</p>
<ul>
<li><code>.assign(할당할 값)</code>
  <img src="https://velog.velcdn.com/images/castle_mi/post/df10d482-449c-43fc-8d87-21e64e9edd67/image.png" alt="">
  이때 dtype은 기존 dtype을 따르며 기존 메모리의 shape과 맞지 않으면 할당할 수 없다.</li>
</ul>
<p><br><br></p>
<h2 id="03_tensor-연산">03_Tensor 연산</h2>
<p><strong>📌 Axis 이해하기</strong></p>
<h3 id="기존-연산자-기호를-사용해도-되는-연산자">기존 연산자 기호를 사용해도 되는 연산자</h3>
<ul>
<li><code>tf.add</code>: 덧셈</li>
<li><code>tf.subtract</code>: 뺄셈</li>
<li><code>tf.multiply</code>: 곱셈</li>
<li><code>tf.divide</code>: 나눗셈</li>
<li><code>tf.pow</code>: n-제곱</li>
<li><code>tf.negative</code>: 음수 부호</li>
</ul>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/1e6aa6ab-9015-418f-b9a5-8ab273ca2cfc/image.png" alt=""></p>
<h3 id="그-외-여러가지-연산자">그 외 여러가지 연산자</h3>
<ul>
<li><code>tf.abs</code>: 절대값</li>
<li><code>tf.sign</code>: 부호</li>
<li><code>tf.round</code>: 반올림</li>
<li><code>tf.ceil</code>: 올림</li>
<li><code>tf.floor</code>: 내림</li>
<li><code>tf.square</code>: 제곱</li>
<li><code>tf.sqrt</code>: 제곱근</li>
<li><code>tf.maximum</code>: 두 텐서의 각 원소에서 최댓값만 반환.</li>
<li><code>tf.minimum</code>: 두 텐서의 각 원소에서 최솟값만 반환.</li>
<li><code>tf.cumsum</code>: 누적합</li>
<li><code>tf.cumprod</code>: 누적곱</li>
</ul>
<br>

<h3 id="axis-이해하기">Axis 이해하기</h3>
<p>기본적인 개념은 가장 바깥에서부터 axis 0이라 하고 안쪽으로 갈수록 axis가 깊어진다고 생각하면 된다.</p>
<p><strong>2차원 텐서</strong></p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/4e1b27eb-73cd-4172-96ef-768667aced43/image.png" alt=""></p>
<p>위와 같은 형태로 되어있어 아래와 같은 값이 나오게 된다.</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/c90f1786-2ace-4acf-b02c-74e09c1c5bd1/image.png" alt=""></p>
<br>

<p><strong>3차원 텐서</strong></p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/82d3becc-54ed-4a64-8068-1b3f39727d6d/image.png" alt=""></p>
<p>그리고 3차원 텐서도 위와 같이 바깥에서부터 axis 0이라고 생각하면 아래와 같이 원하는 위치에 있는 값을 추출할 수 있다.
<img src="https://velog.velcdn.com/images/castle_mi/post/41903f30-3526-4b30-b3a9-aa7ebad7b523/image.png" alt=""></p>
<br>

<h3 id="차원-축소-연산">차원 축소 연산</h3>
<ul>
<li><code>tf.reduce_mean</code>: 설정한 축의 평균을 구한다. </li>
<li><code>tf.reduce_max</code>: 설정한 축의 최댓값을 구한다. </li>
<li><code>tf.reduce_min</code>: 설정한 축의 최솟값을 구한다. </li>
<li><code>tf.reduce_prod</code>: 설정한 축의 요소를 모두 곱한 값을 구한다. </li>
<li><code>tf.reduce_sum</code>: 설정한 축의 요소를 모두 더한 값을 구한다. </li>
</ul>
<br>

<h3 id="행렬-연산">행렬 연산</h3>
<ul>
<li><code>tf.matmul</code>: 내적</li>
<li><code>tf.linalg.inv</code>: 역행렬</li>
</ul>
<br>

<h3 id="🌟-크기-및-차원을-바꾸는-명령">🌟 크기 및 차원을 바꾸는 명령</h3>
<p>그리고 앞서 크기와 차원이 맞지 않으면 연산이 되지 않는다고 언급한 적이 있다. 그런 경우 아래의 명령어를 통해 크기 및 차원을 맞춰준 후 연산을 진행하면 된다.</p>
<ul>
<li><code>tf.reshape</code>: 벡터 행렬의 크기 변환</li>
<li><code>tf.transpose</code>: 전치 연산</li>
<li><code>tf.expand_dims</code>: 지정한 축으로 차원을 추가</li>
<li><code>tf.squeeze</code>: 벡터로 차원을 축소</li>
</ul>
<pre><code class="language-python">a = tf.range(6, dtype=tf.int32)      # [0, 1, 2, 3, 4, 5]
print(&quot;a     :&quot;, a, &quot;\n&quot;)

a_2d = tf.reshape(a, (2, 3))  # 1차원 벡터는 2x3 크기의 2차원 행렬로 변환
print(&quot;a_2d  :&quot;, a_2d, &quot;\n&quot;)

a_2d_t = tf.transpose(a_2d)   # 2x3 크기의 2차원 행렬을 3x2 크기의 2차원 행렬로 변환
print(&quot;a_2d_t:&quot;, a_2d_t, &quot;\n&quot;)

a_3d = tf.expand_dims(a_2d, 0) # 2x3 크기의 2차원 행렬을 1x2x3 크기의 3차원 행렬로 변환
print(&quot;a_3d  :&quot;, a_3d, &quot;\n&quot;)
&#39;&#39;&#39; 
딥러닝의 배치 개념(데이터를 한 묶음으로 바라볼 때)을 이용할 때 차원이 하나 더 필요하므로 
0으로 설정 시, 기존 차원의 맨 앞에 차원 하나를 더 추가해줌
3(-1)으로 설정 시, 기존 차원의 맨 마지막에 차원 하나를 더 추가해줌
&#39;&#39;&#39;
a_4d = tf.expand_dims(a_3d, 3) # 1x2x3 크기의 3차원 행렬을 1x2x3x1 크기의 4차원 행렬로 변환
print(&quot;a_4d  :&quot;, a_4d, &quot;\n&quot;)

a_1d = tf.squeeze(a_4d)        # 1x2x3x1 크기의 4차원 행렬을 1차원 벡터로 변환
print(&quot;a_1d  :&quot;, a_1d, &quot;\n&quot;)   
&#39;&#39;&#39; 
원소의 개수가 1개인 차원을 없애주는 역할
&#39;&#39;&#39;</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/301f57bd-f212-4e5c-aed2-8891bb082c4e/image.png" alt=""></p>
<br>

<h3 id="텐서를-나누거나-두-개-이상의-텐서를-합치는-명령">텐서를 나누거나 두 개 이상의 텐서를 합치는 명령</h3>
<ul>
<li><p><code>tf.slice</code>: 특정 부분을 추출</p>
</li>
<li><p><code>tf.split</code>: 분할
  <img src="https://velog.velcdn.com/images/castle_mi/post/84c533d9-9f30-4b47-9918-e0dd92e2d3dd/image.png" alt=""></p>
</li>
<li><p><code>tf.concat</code>: 합치기(rank가 동일)
  <img src="https://velog.velcdn.com/images/castle_mi/post/4c0bd25d-d4e4-4ef2-98e8-474d2db770de/image.png" alt=""></p>
</li>
<li><p><code>tf.tile</code>: 복제-붙이기</p>
</li>
<li><p><code>tf.stack</code>: 합성(concat과 달리 추가적으로 차원을 붙이며 합쳐줌)
  <img src="https://velog.velcdn.com/images/castle_mi/post/bde1b777-afeb-4d73-bae7-2d97d33e7332/image.png" alt=""></p>
</li>
<li><p><code>tf.unstack</code>: 분리 </p>
</li>
</ul>
<p><br><br><br></p>
<h1 id="2-최적화">2. 최적화</h1>
<h2 id="자동-미분">자동 미분</h2>
<p><code>tf.GradientTape</code></p>
<p>tf.GradientTape는 컨텍스트(context) 안에서 실행된 모든 연산을 테이프(tape)에 &quot;기록&quot;한다. </p>
<p>그 다음 텐서플로는 후진 방식 자동 미분(reverse mode differentiation)을 사용해 테이프에 &quot;기록된&quot; 연산의 그래디언트를 계산하는 흐름으로 진행된다.</p>
<p>딥러닝은 loss라는 스칼라를 모델의 가중치들인 벡터로 미분시키는 작업이므로 스칼라를 벡터로 미분하는 실습을 해보자면 아래와 같다.</p>
<pre><code class="language-python">w = tf.Variable(tf.random.normal((3, 2)), name=&#39;w&#39;)
b = tf.Variable(tf.zeros(2, dtype=tf.float32), name=&#39;b&#39;)
x = [[1., 2., 3.]]


# 하고자하는 연산 진행
# persistent=True : 2번 이상 부를 때 에러가 나지 않기 위함
with tf.GradientTape(persistent=True) as tape:
    y = x @ w + b # 원하는 함수 식
    loss = tf.reduce_mean(y**2) # loss 식

# 미분 계산
# tape.gradient(무엇을 , 뭐로)
# 방법 1. 리스트로 받기
[dl_dw, dl_db] = tape.gradient(loss, [w, b])


# 방법 2. 딕셔너리 형태로 받기
my_vars = {
    &#39;w&#39;: w,
    &#39;b&#39;: b
}

grad = tape.gradient(loss, my_vars)
grad[&#39;b&#39;] # &lt;tf.Tensor: shape=(2,), dtype=float32, numpy=array([ 0.5419482, -1.9879353], dtype=float32)&gt;</code></pre>
<p>위와 같이 </p>
<ol>
<li><code>with</code>문 안에 원하는 loss 계산을 해주고 그것을 <code>tape</code>라 칭한다음, </li>
<li><code>tape.gradient()</code>로 미분값을 받으면 된다.</li>
</ol>
<p>만약에 이때 미분 기록을 하고 싶지 않은 변수가 있다면 <code>trainable</code> 옵션을 False로 설정해주면 된다.
<img src="https://velog.velcdn.com/images/castle_mi/post/d631c55a-5e99-452d-8e79-510eed9f470c/image.png" alt=""></p>
<p>기록되고 있는 variable이 무엇인지 확인하고 싶다면 <code>watched_variables()</code>을 통해 확인 가능!
<img src="https://velog.velcdn.com/images/castle_mi/post/0951f3e8-8d92-41cb-acb6-4695daf3a4fc/image.png" alt=""></p>
<br>

<h2 id="linear-regression">linear regression</h2>
<p>가상의 데이터셋을 만들어 확인해보자.</p>
<p><strong>📌 가상의 데이터셋 준비</strong></p>
<pre><code class="language-python">lr = 0.03

# 실제 가중치의 값
W_true = 3.0
B_true = 2.0 

X = tf.random.normal((500, 1)) # X값
noise = tf.random.normal((500, 1)) # noise 값

y = X * W_true + B_true + noise</code></pre>
<br>

<p>이제 실제값들을 미분하여 해당 가중치들이 나오는지 확인해보자.</p>
<pre><code class="language-python"># 미분을 구하기 위해 사용될 변수들
w = tf.Variable(5.)
b = tf.Variable(0.)

# 학습 과정을 기록하기 위한 변수들
w_records = [w.numpy()]
b_records = [b.numpy()]
loss_records = []

for epoch in range(100): # 500개의 데이터셋을 100번 돌겠다.
    # 매 epoch마다 한 번씩 학습
    with tf.GradientTape() as tape:
        y_hat = X * w + b 
        loss = tf.reduce_mean(tf.square(y - y_hat)) # mse
    dw, db = tape.gradient(loss, [w, b]) # gradient 값이 저장됨

    w.assign_sub(lr * dw) # w.assign(w - lr * dw) 와 동일한 기능
    b.assign_sub(lr * db)

    # 학습 과정 저장
    w_records.append(w.numpy())
    b_records.append(b.numpy())
    loss_records.append(loss.numpy())</code></pre>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/b7717c80-20aa-4de9-8dd1-3d4f328724e7/image.png" alt=""> loss</th>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/0569db76-4dda-462d-ba70-b2a1853e8d9c/image.png" alt=""> w</th>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/6187ef41-5005-4675-aae2-dbe4dcd0f6f0/image.png" alt=""> b</th>
</tr>
</thead>
</table>
<p>loss는 점점 떨어지고 w와 b는 w_true와 b_true에 점점 가까워지는 형태를 띄고 있으므로 잘 학습이 되는 것 같다.</p>
<br>



<h2 id="linear-regression---당뇨병-데이터">linear regression - 당뇨병 데이터</h2>
<p>이번에는 실제 데이터를 이용해 <code>lineaer regression</code>을 학습해보자.</p>
<br>

<p><strong>📌 데이터 준비</strong></p>
<pre><code class="language-python">from sklearn.datasets import load_diabetes
import pandas as pd
import numpy as np

diabetes = load_diabetes()
df = pd.DataFrame(diabetes.data, columns=diabetes.feature_names, dtype=np.float32)
df[&#39;const&#39;] = np.ones(df.shape[0])</code></pre>
<br>

<h3 id="역행렬을-이용한-가중치-구하는-방식">역행렬을 이용한 가중치 구하는 방식</h3>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/090ad5fe-833f-4d11-b44b-9d651837977b/image.png" alt=""></p>
<p>이때 const 변수를 생성해 주었다.
이 덕분에 bias는 const의 가중치가 되므로 우리는 가중치만 고려하면 된다!</p>
<br>

<p><strong>📌 shape 확인</strong></p>
<pre><code class="language-python">diabetes.target.shape # (442,)

X = df
y = np.expand_dims(diabetes.target, axis = 1)

X.shape, y.shape # ((442, 11), (442, 1))</code></pre>
<p>그 전에 target의 차원을 하나 늘려줌으로써 X와 y의 차원을 맞춰주었다.
지금 당장은 에러가 안 날 수 있지만 후 작업에 에러가 날 수 있는 경우의 수를 줄여주기 위해 이렇게 맞춰주는 습관을 길러두는 것이 좋다.</p>
<br>

<p><strong>📌 예측</strong></p>
<pre><code class="language-python"># tf.transpose(X)는 많이 사용되므로 따로 정의해두자.
XT = tf.transpose(X)
w = tf.matmul(tf.matmul(tf.linalg.inv(tf.matmul(XT , X)), XT), y)
y_pred = tf.matmul(X, w)

print(&quot;예측한 진행도 :&quot;, y_pred[0].numpy(),  &quot;실제 진행도 :&quot;, y[0])
print(&quot;예측한 진행도 :&quot;, y_pred[19].numpy(), &quot;실제 진행도 :&quot;, y[19])
print(&quot;예측한 진행도 :&quot;, y_pred[31].numpy(), &quot;실제 진행도 :&quot;, y[31])</code></pre>
<blockquote>
<p> 💻 출력</p>
</blockquote>
<p>예측한 진행도 : [206.11667747] 실제 진행도 : [151.]
예측한 진행도 : [124.01754101] 실제 진행도 : [168.]
예측한 진행도 : [69.47575835] 실제 진행도 : [59.]</p>
<br>

<h3 id="sgd-방식">SGD 방식</h3>
<p><code>SGD</code>를 사용하고 가중치는 <code>Gaussian normal distribution</code> 난수로 초기화된 것을 사용하며 lr는 0.03, 100 iteration을 도는 과정을 코드로 짜보자.</p>
<pre><code class="language-python"># 정의
lr = 0.03
num_iter = 100
w_init = tf.random.normal([df.shape[1], 1], dtype=tf.float64)
w = tf.Variable(w_init)

# 학습 과정을 기록하기 위한 변수들
w_records = [w.numpy()]
loss_records = []

for epoch in range(num_iter): 
    with tf.GradientTape() as tape:
        y_hat = tf.matmul(X, w)
        loss = tf.reduce_mean(tf.square(y - y_hat))
    dw = tape.gradient(loss, w) 

    w.assign_sub(lr * dw) 

    # 학습 과정 저장
    w_records.append(w.numpy())
    loss_records.append(loss.numpy())</code></pre>
<br>

<p><strong>📌 확인</strong></p>
<pre><code class="language-python">plt.plot(loss_records)
plt.title(&#39;loss&#39;)
plt.show()

print(&quot;예측한 진행도 :&quot;, y_hat[0].numpy(),  &quot;실제 진행도 :&quot;, y[0])
print(&quot;예측한 진행도 :&quot;, y_hat[19].numpy(), &quot;실제 진행도 :&quot;, y[19])
print(&quot;예측한 진행도 :&quot;, y_hat[31].numpy(), &quot;실제 진행도 :&quot;, y[31])</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/fd70965b-8fdb-420b-ae71-9c519025701b/image.png" alt="">
예측한 진행도 : [155.91650454] 실제 진행도 : [151.]
예측한 진행도 : [146.29034167] 실제 진행도 : [168.]
예측한 진행도 : [140.40297749] 실제 진행도 : [59.]</p>
<br>

<h2 id="퍼셉트론">퍼셉트론</h2>
<p><a href="https://velog.io/@shlee0125/%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-%EC%A0%95%EB%A6%AC-%ED%8D%BC%EC%85%89%ED%8A%B8%EB%A1%A0-Perceptron">👀 퍼셉트론 이해에 도움을 받은 사이트</a></p>
<p>퍼셉트론은 classification에 쓰이는 모델 중 하나로,
입력특성에 대한 가중합이 threshold를 넘는지 아닌지를 기준으로 1 또는 -1의 클래스를 예측하는 모델이다.</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/7d7ff94b-43c6-4546-8523-962ebe08bf83/image.png" alt=""></p>
<p>따라서 비용함수로 쓰는 아래의 식은</p>
<p>$$
\large{
Loss = \sum_{i=1}^N \max(0, -y_i \hat{y_i})
}
$$</p>
<p>실제값과 예측값이 다른 데이터의 개수로도 생각할 수 있는데 그 이유는 아래와 같다.</p>
<ul>
<li><p>실제값과 예측값이 같은 경우 (-1, -1) 또는 (1, 1)가 되므로 $-y_i \hat{y_i}$ 는 음수가 된다. 따라서 Loss는 0이 될 것이다.</p>
</li>
<li><p>반대로 실제값과 예측값이 다른 경우((-1, 1) 또는 (1, -1))에  $-y_i \hat{y_i}$는 양수가 되므로 Loss는 1이 될 것이다. </p>
</li>
</ul>
<p>iris 데이터를 이용해 <code>Perceptron</code>을 구현해보자.</p>
<br>

<p><strong>📌 데이터 준비</strong></p>
<pre><code class="language-python">from sklearn.datasets import load_iris
iris = load_iris()

idx = np.in1d(iris.target, [0, 2]) # setosa와 virginica 클래스만 사용
X_data = iris.data[idx, 0:2] # feature로 sepal 컬럼만 사용
y_data = (iris.target[idx] - 1.0)[:, np.newaxis] 

X_data.shape, y_data.shape # ((100, 2), (100, 1))</code></pre>
<br>

<p>*<em>📌 Perceptron 구현 *</em></p>
<p>데이터 하나당 한 번씩 weights가 업데이트하고 <code>learning_rate</code>는 0.0003, <code>iteration</code> 은 500 인 Perceptron을 구현해보자.</p>
<pre><code class="language-python"># 정의
num_iter = 500
lr = 0.0003


# 가중치 초기 설정
w_init = tf.random.normal([X_data.shape[1], 1], dtype=tf.float64)
w = tf.Variable(w_init)

b_init = tf.random.normal([1, 1], dtype=tf.float64)
b = tf.Variable(b_init)

# Perceptron
zero = tf.constant(0, dtype=tf.float64) # 그냥 0으로 써도 됨

for epoch in range(num_iter):
    # 데이터 하나당
    for i in range(X_data.shape[0]):
        # shape을 맞춰주기 위함
        x = X_data[i:i+1] #expand_dims를 써도 됨
        y = y_data[i:i+1]

        with tf.GradientTape() as tape:
            y_hat = tf.tanh(tf.matmul(x, w) + b)
            loss = tf.maximum(zero, tf.multiply(-y, y_hat))
        dw, db = tape.gradient(loss, [w, b]) 

        w.assign_sub(lr * dw) 
        b.assign_sub(lr * db) 

# 확인
y_pred = tf.tanh(tf.matmul(X_data, w) + b)
print(&quot;예측치 :&quot;, -1 if y_pred[0] &lt; 0 else 1,  &quot;정답 :&quot;, y_data[0])
print(&quot;예측치 :&quot;, -1 if y_pred[51] &lt; 0 else 1, &quot;정답 :&quot;, y_data[51])
print(&quot;예측치 :&quot;, -1 if y_pred[88] &lt; 0 else 1, &quot;정답 :&quot;, y_data[88])</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>예측치 : -1 정답 : [-1.]
예측치 : 1 정답 : [1.]
예측치 : 1 정답 : [1.]</p>
<p>그리고 -1과 1로 분류될 수 있도록 예측치를 작업해주고 실제값과 비교해서 성능을 평가하면 된다.</p>
<p><br><br></p>
<h1 id="간단한-모델-학습하기">간단한 모델 학습하기</h1>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/32180f86-3d9c-4e8d-be59-ce86f44d376a/image.png" alt=""></p>
<p>일반적으로 딥러닝 프로젝트를 한다면 위와 같은 4단계의 흐름으로 프로젝트가 진행될 것이다. 이번에는 전반적인 흐름에 대해 알아보자.</p>
<h2 id="deep-learning-flow">Deep Learning Flow</h2>
<p>MNIST 데이터를 실습해보자.</p>
<p>먼저, <code>.shape</code> , <code>.dtype</code>을 이용해 <strong>데이터의 shape과 dtype을 확인</strong>해주어야한다.(기본 중에 기본!!!)</p>
<p>그리고 하나의 데이터를 추출하여 데이터가 어떻게 생겼는지 볼 필요가 있다.</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/f5253b8d-2c52-4478-afdf-8f2b1ec5d77d/image.png" alt=""></p>
<p>이렇듯 데이터를 받으면 데이터를 이해하기 위해 많은 노력을 할 것...!!</p>
<p>데이터셋에 <strong>각 숫자의 그림이 몇 개씩 얼만큼 들어있는지 확인(target 데이터 분포 확인)</strong>도 할 수 있다.</p>
<pre><code class="language-python">y_unique, y_counts = np.unique(train_y, return_counts=True)</code></pre>
<p>물론 이렇게 보는 것보단, 데이터프레임화하거나 시각화하여 보는게 더 좋겠지??</p>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/1b5ecb01-77f9-416d-8f9e-7867943d1833/image.png" alt=""></th>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/1adf6b73-4056-46d2-8f0a-ef874eeccc08/image.png" alt=""></th>
</tr>
</thead>
</table>
<br>

<h2 id="preprocessing">Preprocessing</h2>
<p><code>Preprocessing</code> 단계에는 데이터 검증, 전처리, 데이터 증강 등이 포함된다. 이런 작업들을 거쳐 모델의 입력값이 될 수 있게끔 변형시켜주는 것이 이 단계의 목적이다.</p>
<h3 id="데이터-검증">데이터 검증</h3>
<p>데이터 검증이란, 데이터 중에 학습에 포함되면 안 되는 것이 있는지 , (개인정보가 들어간 데이터, 테스트용 데이터, 중복 데이터 등) 학습 의도와 다른 데이터가 있는지, 라벨이 잘못된 데이터가 있는지를 확인하는 과정이다.</p>
<p>예를 들어 아래의 코드는 픽셀 값이 이상한 데이터를 걸러내는 작업으로 아래와 같이 함수를 적용시켜 데이터 검증을 할 수 있다.</p>
<pre><code class="language-python"># preprocessing할 때 로직에 대한 실수를 하지 않기 위해
# 함수와 클래스로 구현을 많이 함

def validate_pixel_scale(x):
    return 255 &gt;= x.max() and 0 &lt;= x.min() # 픽셀값이 이상한 데이터는 걸러내는 작업

validated_train_x = np.array([x for x in train_x if validate_pixel_scale(x)])
validated_train_y = np.array([y for x, y in zip(train_x, train_y) if validate_pixel_scale(x)])

# 걸러진 데이터가 있는지 shape 확인
print(validated_train_x.shape)
print(validated_train_y.shape)</code></pre>
<br>

<h3 id="전처리">전처리</h3>
<p>전처리 단계는 입력하기 전에 모델링에 적합하게 처리하기 위한 단계로 Scaling, Resizing, label encoding 등이 있다. 
이 또한 전처리 후 <code>dtype</code>, <code>shape</code> 체크는 기본!!</p>
<p>아래는 Scaling을 진행한 과정이다.</p>
<pre><code class="language-python">def scale(x):
    return (x / 255.0).astype(np.float32) # 0 ~ 1사이의 값을 갖도록

# 확인
sample = scale(validated_train_x[777])

sample.max(), sample.min() # 직접 확인해보거나

sns.displot(sample) # 시각화하는 방법도 있다.

# 실제 데이터에 대한 실행 코드
scaled_train_x = np.array([scale(x) for x in validated_train_x])</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>(1.0, 0.0)
<img src="https://velog.velcdn.com/images/castle_mi/post/8460e0ea-b9a7-442c-87f5-9bd92dc5226c/image.png" alt=""></p>
<br>

<h3 id="flattening">Flattening</h3>
<p>가끔 사용하는 모델에 따라 1차원 벡터가 input이 되는 레이어들이 있다. 이런 경우 모델에 입력하기 전 <code>flatten</code> 해주는 작업이 필요하다.</p>
<pre><code class="language-python"># 실행
flattend_train_x = scaled_train_x.reshape(60000, -1)

# 확인
flattend_train_x.shape # (60000, 784)</code></pre>
<br>

<h3 id="label-encoding">Label Encoding</h3>
<p>타겟 데이터가 범주형인 경우 &#39;frog&#39;, &#39;deer&#39;와 같은 이름이 들어간 것은 훈련에 사용할 수 없다. 이런 데이터가 받았을 땐 이를 카테고리 데이터로 변형시켜주어야한다.
그 중 하나가 <code>One-Hot Encoding</code> 작업으로 각 클래스에 해당하면 1, 나머지 클래스에 대해서는 0으로 매겨주는 작업이다.</p>
<pre><code class="language-python">tf.keras.utils.to_categorical(5, num_classes=10)</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>array([0., 0., 0., 0., 0., 1., 0., 0., 0., 0.], dtype=float32)</p>
<pre><code class="language-python"># 실행
ohe_train_y = np.array([tf.keras.utils.to_categorical(y, num_classes=10) for y in validated_train_y])

# 확인
ohe_train_y.shape # (60000, 10)</code></pre>
<br>

<p>보통 이런 큰 작업들은 하나의 클래스로 만들어 보관함으로써 모든 데이터에 동일하게 적용하게끔 관리하고 있다.</p>
<pre><code class="language-python">class DataLoader():
    def __init__(self):
        # 데이터 불러오기
        (self.train_x, self.train_y), \
            (self.test_x, self.test_y) = tf.keras.datasets.mnist.load_data()

    def validate_pixel_scale(self, x):

        return 255 &gt;= x.max() and 0 &lt;= x.min()

    def scale(self, x):
        &quot;&quot;&quot;
        Make pixels within 0 ~ 1

        return 
            scaled image (dtype=float32)
        &quot;&quot;&quot;
        return (x / 255.0).astype(np.float32)

    def preprocess_dataset(self, dataset):
        &quot;&quot;&quot;
        feature 
            shape : (num_data, 28, 28)
        target 
            shape : (num_data,)

        return 
            feature 
                shape : (num_data, 28, 28)
            target 
                shape : (num_data,)
        &quot;&quot;&quot;
        (feature, target) = dataset

        validated_x = np.array(
            [x for x in feature if self.validate_pixel_scale(x)])

        validated_y = np.array([y for x, y in zip(feature, target)
                                if self.validate_pixel_scale(x)])

        # scaling #
        scaled_x = np.array([self.scale(x) for x in validated_x])

        # flattening #
        flattend_x = scaled_x.reshape((scaled_x.shape[0], -1))

        # label encoding #
        ohe_y = np.array([tf.keras.utils.to_categorical(
            y, num_classes=10) for y in validated_y])

        return flattend_x, ohe_y

    def get_train_dataset(self):
        return self.preprocess_dataset((self.train_x, self.train_y))

    def get_test_dataset(self):
        return self.preprocess_dataset((self.test_x, self.test_y))</code></pre>
<p>위와 같이 클래스로 작성해주고 아래와 같이 객체를 생성하여 코드를 실행시켜주면 된다.</p>
<pre><code class="language-python"># 객체 생성
mnist_loader = DataLoader()

# train 데이터에 대해 실시
train_x, train_y = mnist_loader.get_train_dataset()

# test 데이터에 대해 실시
test_x, test_y = mnist_loader.get_test_dataset()

# 확인
print(train_x.shape, train_x.dtype)
print(train_y.shape, train_y.dtype)
print(test_x.shape, test_x.dtype)
print(test_y.shape, test_y.dtype)</code></pre>
<p><br><br></p>
<h2 id="modeling">Modeling</h2>
<p><code>Modeling</code>에는 크게 3가지의 과정이 있다.</p>
<ol>
<li>모델 정의</li>
<li>학습 로직 - 비용함수, 학습 파라미터 세팅</li>
<li>학습</li>
</ol>
<p>3단계를 한 번 실습해보자.</p>
<h3 id="1-모델-정의">1. 모델 정의</h3>
<p><code>tensorflow</code>에는 모델을 정의할 수 있는 방법이 여러가지가 있는데 지금은 그 첫 번째 방법인 <code>Sequential</code>을 통해 생성해보자.
<code>.add</code>를 통해 레이어를 추가해주면 된다.</p>
<pre><code class="language-python">from tensorflow.keras.layers import Dense, Activation

model = tf.keras.Sequential()
# .add를 통해 레이어를 추가할 수 있음
model.add(Dense(15, input_dim = 784))
model.add(Activation(&#39;sigmoid&#39;))
model.add(Dense(10)) # output dim에 맞게 설정
model.add(Activation(&#39;softmax&#39;))

# 모델 정보 확인
model.summary()</code></pre>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/8fb3c379-4593-47a9-974e-0ed721a8b6ac/image.png" alt=""></p>
<br>

<h3 id="2-학습-로직">2. 학습 로직</h3>
<p>이 단계에서는 <code>optimizer</code>와 <code>loss</code> 를 정의하고 <code>compile</code> 하는 과정을 말한다.</p>
<pre><code class="language-python">learning_rate = 0.03
opt = tf.keras.optimizers.SGD(learning_rate) # 최적화
loss = tf.keras.losses.categorical_crossentropy # loss

# 모델 정의(최적화를 어떻게 하고, loss 함수는 무엇을 할건지, 모델의 성능의 지표는 무엇으로 할건가)
model.compile(optimizer=opt, loss=loss, metrics=[&quot;accuracy&quot;])</code></pre>
<br>

<h3 id="3-학습">3. 학습</h3>
<p>학습은 <code>.fit</code>을 통해 진행할 수 있다.</p>
<pre><code class="language-python"># .fit
hist = model.fit(train_x, train_y, epochs = 10, batch_size=256)</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/3da1402f-5b4a-424b-bd98-c4eae0d0b899/image.png" alt=""></p>
<br>

<br>

<h2 id="evaluation">Evaluation</h2>
<p>학습을 다 했다면 모델을 평가하는 과정이 필요하다.</p>
<p>이 단계도 </p>
<ol>
<li>학습 과정을 추적하고</li>
<li>Test 데이터에 대해 모델을 다시 또 검증해보고</li>
<li>후처리하는</li>
</ol>
<p>단계로 이루어진다. </p>
<h3 id="1-학습-과정-추적">1. 학습 과정 추적</h3>
<pre><code class="language-python">plt.figure(figsize = (10, 5))
plt.subplot(121)
plt.plot(hist.history[&#39;loss&#39;])
plt.subplot(122)
plt.plot(hist.history[&#39;accuracy&#39;])
plt.show()</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/003fa5b1-a74d-41c5-a0a5-9f4a53d9c6bc/image.png" alt=""></p>
<p>기본적으로 위와 같이 시각화하는 방식이 있다. </p>
<p>원래 loss가 어느정도 떨어지고 유지되는 구간이 있어야 안정적으로 수렴했다고 보는데 계속 떨어지는 추세를 보이고 있으므로 더 학습시킬 필요가 있구나! 라는 인사이트를 얻어갈 수 있다.</p>
<br>

<h3 id="2-모델-검증">2. 모델 검증</h3>
<p>Test 데이터에 대해 모델을 검증해보는 과정으로 <code>.evaluate</code>를 사용하면 된다.</p>
<pre><code class="language-python"># .evaluate
model.evaluate(test_x, test_y)</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/644e822d-afa4-4ef0-8af1-2b13e9a54706/image.png" alt=""></p>
<p>위와 같이 Test 데이터에 대한 loss와 acc을 반환해준다.</p>
<br>

<h3 id="3-후처리">3. 후처리</h3>
<p>만약 학습이 만족스럽게 되었다면 실제로 배포하거나, 만족스럽지 못했다면 모델을 수정할 수 있을 것이다.</p>
<p>학습이 만족스러웠다는 가정하에 Test 데이터에 대해 어떻게 예측을 했는지 확인을 할 수 있는데 <code>.predict</code>를 통해 예측을 해주자.</p>
<pre><code class="language-python"># .predict
pred = model.predict(test_x[:1])
pred.argmax()</code></pre>
<p>이 문제는 분류 문제이므로 <code>argmax</code>를 통해 가장 큰 값을 가지는 인덱스가 무엇인지 추출해주어야한다.</p>
<p>그리고 실제 데이터와 맞는지 확인해보면 된다.</p>
<pre><code class="language-python">test_y[0] # 이렇게 확인해도 되지만
# array([0., 0., 0., 0., 0., 0., 0., 1., 0., 0.], dtype=float32)

# 이미지로 확인하는 것이 더 좋음
sample_image = test_x[0].reshape((28, 28)) * 255 # 복구

plt.imshow(sample_image)
plt.show()</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/15d799eb-7728-4d71-8ecf-64ad87d2c544/image.png" alt=""></p>
<br>

]]></description>
        </item>
        <item>
            <title><![CDATA[[딥러닝] Autoencoder 및 이미지 증강 / YOLO 모델 이해 ]]></title>
            <link>https://velog.io/@castle_mi/%EB%94%A5%EB%9F%AC%EB%8B%9D-Autoencoder-%EB%B0%8F-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%A6%9D%EA%B0%95-YOLO-%EB%AA%A8%EB%8D%B8-%EC%9D%B4%ED%95%B4</link>
            <guid>https://velog.io/@castle_mi/%EB%94%A5%EB%9F%AC%EB%8B%9D-Autoencoder-%EB%B0%8F-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%A6%9D%EA%B0%95-YOLO-%EB%AA%A8%EB%8D%B8-%EC%9D%B4%ED%95%B4</guid>
            <pubDate>Thu, 02 Nov 2023 16:20:14 GMT</pubDate>
            <description><![CDATA[<p>✍🏻 2일 공부 이야기.</p>
<p><a href="https://github.com/castlemi99/ZeroBaseDataSchool/blob/main/Deep%20Learning/7.%20Autoencoder%20%26%20Image%20Augmentation.ipynb"><img src="https://velog.velcdn.com/images/castle_mi/post/8ae373a8-1cfe-4c56-89e6-734aae311b47/image.png" alt=""></a></p>
<p>오늘 실습한 코드 내용은 위 깃허브 사진을 클릭하면 이동합니다 :)</p>
<br>

<h1 id="autoencoders--image-augmentation">Autoencoders , Image Augmentation</h1>
<h2 id="오토인코더">오토인코더</h2>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/3c139d8b-88a4-40d9-96b7-f91fbfce5a04/image.png" alt=""></p>
<p>입력에서부터 제일 작아지는 지점까지를 Encoder라고 하고 다시 출력지점까지를 Decoder라고 한다. </p>
<p>이때 제일 작아진 지점(붉은색)을 Latent Vector(잠재 변수)라 부른다.</p>
<p>Encoder는 일종의 특징 추출기 같은 역할이고
Decoder는 압축된 데이터를 다시 복원하는 역할을 하고
AutoEncoder는 자기 자신을 재생성하는 모델로, 입력과 출력이 동일하다.</p>
<h3 id="mnist">MNIST</h3>
<h2 id="이미지-증강">이미지 증강</h2>
<p><a href="https://www.tensorflow.org/tutorials/images/data_augmentation?hl=ko"><img src="https://velog.velcdn.com/images/castle_mi/post/eb5c0268-ea10-4a75-ac4d-789f576f4656/image.png" alt=""></a></p>
<p><br><br><br></p>
<h1 id="yolo">YOLO</h1>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/587c11a8-b20d-4a18-8f82-d8b67cd2a2e2/image.png" alt=""></p>
<p>먼저 딥러닝의 이미지의 분류에는 크게 위와 같이 구분된다.</p>
<ul>
<li><strong>Image Classification</strong> : 이미지에서 가장 잘 두드러지는 사물 하나를 찾음<ul>
<li>입력 이미지에 대해 하나의 클래스 레이블을 할당</li>
</ul>
</li>
<li><strong>Object Localization</strong> : 이미지에서 해당 사물의 클래스와 위치를 찾음<ul>
<li>이미지 내에 존재하는 사물의 위치를 바운딩 박스(bounding box)로 표시하고, 해당 사물의 클래스를 분류</li>
</ul>
</li>
<li><strong>(Multiple) Object Detection</strong> : 이미지에서 여러 사물의 클래스를 찾음<ul>
<li>이미지 내에 존재하는 여러 사물의 위치를 바운딩 박스로 표시하고, 각 사물의 클래스를 분류</li>
</ul>
</li>
<li><strong>Semantic Segmentation</strong> : 이미지에서 각 사물을 클래스별로 위치를 찾음(같은 클래스는 하나로 파악)<ul>
<li>같은 클래스의 사물들은 하나로 파악하는 것이 아니라, 각 픽셀 단위로 클래스를 분류하여 이미지를 세분화</li>
</ul>
</li>
<li><strong>Instance Segmentation</strong> : 이미지에서 각 사물마다 위치를 찾음(같은 클래스도 다른 위치에 있다면 다르게 파악)<ul>
<li>같은 클래스라도 서로 다른 인스턴스에 대해 개별적으로 위치를 파악</li>
</ul>
</li>
<li><strong>Keypoint Detection</strong> : 이미지에서 특정한 위치나 물체의 핵심적인 지점, 예를 들어 얼굴에서 눈, 코, 입 등을 찾음.</li>
</ul>
<br>

<p><img src="https://velog.velcdn.com/images/castle_mi/post/2878ba12-5d5e-485a-841c-2eae6f313c6a/image.png" alt=""></p>
<p>그리고 이미지 분류의 모델들이 주목받기 시작한 시점을 그래프로 표현해본 것인데, 분류 모델에 따라 1 stage Detector와 2 stage Detector로 구분된다.</p>
<h2 id="1-stage-detector와-2-stage-detector">1 stage Detector와 2 stage Detector</h2>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/6fae2972-b6e6-41f5-b975-a14c83612d06/image.png" alt=""></p>
<ul>
<li><p><strong>2 stage Detector</strong>
  : 2 stage Detector는 이미지를 입력받으면 먼저 사물이 위치한 곳을 찾아내는 작업을 한다.(= Region Proposal) 그 후 해당 사물이 무엇인지 분류하는 작업을 진행한다.(= Classification)
  이렇듯 2번 딥러닝 모델이 돌기 때문에 2 stage라는 이름이 붙여졌다.
  당연히 속도가 느리다.</p>
</li>
<li><p><strong>1 stage Detector</strong>
  : 위와 달리, 1 stage Detector는 딥러닝이 1번만 돈다.
  먼저 이미지를 입력받으면 이미지를 잘게 그리드 형태로 쪼개고 각 그리드에 대해 Detection을 진행하는데 이때 같은 사물이면 연결짓는 흐름으로 진행된다.
  <strong>Y</strong>ou <strong>O</strong>nly <strong>L</strong>ook <strong>O</strong>nce 라는 의미를 가진 YOLO도 1 stage Detector에 해당한다.</p>
</li>
</ul>
<br>

<h2 id="yolo-1">YOLO</h2>
<p>우리가 1 stage Detector에서 주목해서 보아야할 점은
<em>Multi class를 object detection을 하는데 어떻게 1 stage로 했는가?</em> 이다.</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/d7294dca-be6e-4f58-9699-169fa79a82bd/image.png" alt=""></p>
<p>그러므로 우리가 주목해서 볼 점은 </p>
<ul>
<li>Multiclass는 classification으로 해결하고</li>
<li>Bounding Box는 Regressor로 해결한 </li>
</ul>
<p>이 부분이다.</p>
<br>

<p><img src="https://velog.velcdn.com/images/castle_mi/post/0fec768d-af35-4773-93a1-30e7cf9806b6/image.png" alt=""></p>
<p>먼저 YOLO의 전체적인 흐름은 위 이미지를 생각하면 된다.</p>
<p>1 stage이기 때문에 YOLO는 한 번에 위 작업을 모두 수행하게 되는데</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/ac90e93b-bf9f-4baf-bd60-513e5fc1e980/image.png" alt=""></p>
<p>대략적인 흐름은 위 사진을 참고하자.</p>
<p>classification 전까지 YOLO는 <code>GoogleNet</code>을 변형한 형태를 띄고 있다.</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/9ea31136-b9eb-4f5f-9332-89b0b0c93ab0/image.png" alt=""></p>
<p>그리고 4번 과정에 대해 논문에서는 위와 같이 설명하여 강의에서도 위 자료로 설명을 해주셨는데 잘 이해가 되질 않았다  🫠</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/e54d7071-fca8-4a17-90d8-26e4eb5fa9e2/image.png" alt=""></p>
<p><a href="https://dotiromoook.tistory.com/24">👀 YOLO 이해에 도움이 된 사이트</a></p>
<p>위 사이트에서는 사진과 같이 설명을 이해하기 쉽게 해두셔서 YOLO를 이해하는데 많은 도움을 받았다 😚</p>
<br>

<h3 id="실제로-사용해보기">실제로 사용해보기</h3>
<p><a href="https://github.com/RahmadSadli/Deep-Learning/tree/master/YOLOv3_TF2"><img src="https://velog.velcdn.com/images/castle_mi/post/7c0e97b9-672d-40aa-829e-2f5a9a10b1a3/image.png" alt=""></a></p>
<p><code>YOLOv3</code> 를 배포하신 원작자분의 깃허브이다.</p>
<p>이를 clone하거나 다운받으면 되는데 나는 colab에 clone해주었다.</p>
<p><a href="https://velog.io/@yookyungkho/Tip2.-colab%EC%97%90%EC%84%9C-github-%EC%86%8C%EC%8A%A4%EC%BD%94%EB%93%9C-%ED%81%B4%EB%A1%A0%ED%95%98%EA%B8%B0">👀 colab에 깃허브 clone할 때 참고한 사이트</a></p>
<p>위 사이트를 참고하여 아래와 같이 순서대로 실행해본 결과, 
<img src="https://velog.velcdn.com/images/castle_mi/post/c2a789d0-e083-45f1-ae5a-70f38369cded/image.png" alt="">
3번과 같이 나의 구글 드라이브에 <code>YOLO v3</code>가 clone되었다. </p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/e276169b-5812-4b42-a3a3-11ab041fa7c4/image.png" alt=""></p>
<p>그리고 <code>image.py</code>, <code>video.py</code>, <code>yolov3.py</code> 파일을 열어 GPU를 가동시키는 부분을 주석처리 해주었다.</p>
<p>실제 모델을 학습시킬 때는 GPU가 필요하지만 우리는 그냥 이용만 할거라서 CPU에서도 충분히 사용가능하기 때문에 위와 같은 설정을 해주었다.</p>
<br>

<p>그 다음 <code>!python convert_weight.py</code>을 실행시키려했는데 <code>convert_weight.py</code>라는 파일이 없다고 떴다.</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/1d33b0e2-3d0d-4e2a-8d5a-6a8ec6268996/image.png" alt=""></p>
<p>원작자의 깃허브에 <code>weights</code> 폴더의 글을 보니, 위와 같이 쓰여있었고 해당 링크를 통해 다운받은 <code>convert_weight.py</code> 파일을 <code>weights</code> 폴더에 넣어준 후 다시 실행시켰다.</p>
<br>

<p>(직접 실습해보려고 했는데.. 자꾸 실패해서 일단 시간 상 강의 내용만 학습했다)</p>
<br>
<br>
<br>

]]></description>
        </item>
        <item>
            <title><![CDATA[[딥러닝] 식물 잎의 사진으로 질병 분류하기(전이학습을 배워보자 / 텐서플로 허브)]]></title>
            <link>https://velog.io/@castle_mi/%EB%94%A5%EB%9F%AC%EB%8B%9D-%EC%8B%9D%EB%AC%BC-%EC%9E%8E%EC%9D%98-%EC%82%AC%EC%A7%84%EC%9C%BC%EB%A1%9C-%EC%A7%88%EB%B3%91-%EB%B6%84%EB%A5%98%ED%95%98%EA%B8%B0%EC%A0%84%EC%9D%B4%ED%95%99%EC%8A%B5%EC%9D%84-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90-%ED%85%90%EC%84%9C%ED%94%8C%EB%A1%9C-%ED%97%88%EB%B8%8C</link>
            <guid>https://velog.io/@castle_mi/%EB%94%A5%EB%9F%AC%EB%8B%9D-%EC%8B%9D%EB%AC%BC-%EC%9E%8E%EC%9D%98-%EC%82%AC%EC%A7%84%EC%9C%BC%EB%A1%9C-%EC%A7%88%EB%B3%91-%EB%B6%84%EB%A5%98%ED%95%98%EA%B8%B0%EC%A0%84%EC%9D%B4%ED%95%99%EC%8A%B5%EC%9D%84-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90-%ED%85%90%EC%84%9C%ED%94%8C%EB%A1%9C-%ED%97%88%EB%B8%8C</guid>
            <pubDate>Thu, 02 Nov 2023 05:25:46 GMT</pubDate>
            <description><![CDATA[<p>✍🏻 1일 공부 이야기.</p>
<p><a href="https://github.com/castlemi99/ZeroBaseDataSchool/blob/main/Deep%20Learning/6_Transfer_Learning.ipynb"><img src="https://velog.velcdn.com/images/castle_mi/post/06e6460e-7c6f-4a99-9f79-38c8e13b8543/image.png" alt=""></a>
오늘 실습한 코드 내용은 위 깃허브에 업로드해두었습니다. 사진을 클릭하면 이동해요 !</p>
<br>

<h1 id="식물-잎의-사진으로-질병-분류하기전이학습을-배워보자">식물 잎의 사진으로 질병 분류하기(전이학습을 배워보자)</h1>
<h2 id="파일-정리">파일 정리</h2>
<p>일단 캐글에서 받은 데이터라고 해서 캐글 페이지를 찾고 다운로드 없이 코랩에서 바로 불러오는 작업을 하려고 했는데에.. 캐글 페이지를 찾지 못했다 😭</p>
<p>언젠가 쓸 수도 있을테니 링크는 첨부해둬야징</p>
<p><a href="https://blog.naver.com/comedu03/222429703891"><strong>📌 캐글 데이터 코랩에서 불러오기</strong></a></p>
<p>여튼, 그러면 일단 강의자료에 있는 구글 드라이브의 데이터를 사용할 수 밖에 없는데 그렇게 되면 다운을 받아야되나? 싶었다. </p>
<p>다운로드 받는데 3, 4시간... 이 걸린다길래 이건 아니다하고 바로 종료 후 방법을 찾았다.</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/bf3f6f71-cc2e-4927-9114-4dca8010af63/image.png" alt=""></p>
<p>이번에는 GPU를 사용하기 위해 colab에서 실습할꺼라 위 사진의 <strong>드라이브 바로가기</strong> 를 눌러 내 드라이브에 바로 추가해주었다.</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/9bc75ddb-cc3a-4f66-8e5c-aa7d74ad5727/image.png" alt=""></p>
<p>그리고 코랩에서 1번 아이콘을 눌러 마운트를 시켜주고(코랩에서 드라이브 환경에 있는 파일을 쓸 수 있게 해줌) 방금 추가해준 <code>dataset.zip</code>의 경로를 복사해준다. (마우스 우클릭 - 경로 복사)</p>
<br>

<p><strong>📌 압축 파일 풀기</strong></p>
<pre><code class="language-python"># 데이터 압축 파일 풀기
!unzip -qq &#39;/content/drive/MyDrive/제로베이스스쿨 공부/Deep Learning/data/dataset.zip&#39; -d &#39;./dataset&#39;</code></pre>
<br>

<p><strong>📌 데이터 정리</strong></p>
<p>현재 폴더에는 각 클래스로 구분된 폴더에 사진이 여러 장 있다.
따라서 Train / Test / Val 폴더로 구분된 데이터용 폴더를 하나 새로 만들어주고 각 폴더에 랜덤으로 사진을 넣어주는 작업을 실시해보려고 한다.</p>
<ul>
<li>폴더 생성<pre><code class="language-python">import os
</code></pre>
</li>
</ul>
<p>original_dataset_dir = &#39;./dataset&#39;
classes_list = os.listdir(original_dataset_dir) # 데이터셋이 있는 폴더</p>
<p>base_dir = &#39;./splitted&#39; # 나중에 Train, Test, Val 데이터를 분리시킬 폴더
os.mkdir(base_dir)</p>
<h1 id="데이터-정리를-위한-목록-및-폴더-생성">데이터 정리를 위한 목록 및 폴더 생성</h1>
<p>import shutil</p>
<h2 id="base_dir-하위에-각각의-폴더-생성">base_dir 하위에 각각의 폴더 생성</h2>
<p>train_dir = os.path.join(base_dir, &#39;train&#39;)
os.mkdir(train_dir)</p>
<p>validation_dir = os.path.join(base_dir, &#39;val&#39;)
os.mkdir(validation_dir)</p>
<p>test_dir = os.path.join(base_dir, &#39;test&#39;)
os.mkdir(test_dir)</p>
<p>for cls in classes_list: # 생성된 폴더에도 기존 데이터에 있던 하위 폴더들 생성 
  os.mkdir(os.path.join(train_dir, cls))
  os.mkdir(os.path.join(validation_dir, cls))
  os.mkdir(os.path.join(test_dir, cls))</p>
<pre><code>
&lt;br&gt;

- 생성된 폴더에 데이터 넣기
```python
# 데이터 확인
import math

for cls in classes_list:
    path = os.path.join(original_dataset_dir, cls) # 기존 데이터의 폴더명
    fnames = os.listdir(path) # path 폴더에 있는 파일명 저장 

    # 불러온 파일들을 6:2:2로 train / val / test로 분리
    train_size = math.floor(len(fnames) * 0.6)
    validation_size = math.floor(len(fnames) * 0.2)
    test_size = math.floor(len(fnames) * 0.2)

    # 각 인덱스에 해당하는 파일들을 각 폴더에 저장 
    train_fnames = fnames[:train_size]
    print(&quot;Train size((&quot;,cls,&quot;) :&quot;, len(train_fnames))
    for fname in train_fnames:  
        src = os.path.join(path, fname) # 기존 파일 경로
        dst = os.path.join(os.path.join(train_dir, cls), fname) # 카피할 파일 경로
        shutil.copyfile(src, dst) # 새로 생성된 train dir에 파일 저장

    validation_fnames = fnames[train_size:(validation_size + train_size)]
    print(&quot;Validation size((&quot;,cls,&quot;) :&quot;, len(validation_fnames))
    for fname in validation_fnames:  
        src = os.path.join(path, fname)
        dst = os.path.join(os.path.join(validation_dir, cls), fname)
        shutil.copyfile(src, dst) # 새로 생성된 validation dir에 파일 저장

    test_fnames = fnames[(train_size + validation_size):(validation_size + train_size + test_size)]
    print(&quot;Test size((&quot;,cls,&quot;) :&quot;, len(test_fnames))
    for fname in test_fnames:  
        src = os.path.join(path, fname)
        dst = os.path.join(os.path.join(test_dir, cls), fname)
        shutil.copyfile(src, dst) # 새로 생성된 test dir에 파일 저장</code></pre><table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/6a0910f3-8e69-4bbd-a817-d1b7a88e6789/image.png" alt=""></th>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/8aa5dcf4-b8ff-48b9-9987-ac7f0390c12f/image.png" alt=""></th>
</tr>
</thead>
</table>
<p>위 코드를 실행시키면 사진과 같이 <code>splitted</code> 폴더 아래 test / train / val 폴더가 생기고 각 폴더 안에 랜덤으로 섞인 사진들이 들어간 것을 볼 수 있다.</p>
<p><br><br></p>
<h2 id="학습">학습</h2>
<br>

<p><strong>📌 GPU에서 작업하기</strong></p>
<p>코랩에서 이번 실습을 하게 된 이유는 바로 cuda 환경에서 작업하기 위함이다.
<img src="https://velog.velcdn.com/images/castle_mi/post/c5c74b22-91ec-49fb-83cb-f809c3d01558/image.png" alt="">
먼저 GPU를 사용할 수 있도록 [런타임] - [런타임 유형 변경] 을 클릭하고 GPU를 선택해준 후 </p>
<p>아래 코드를 실행시켜 GPU를 사용할 수 있는지 확인해보자.</p>
<pre><code class="language-python">import torch
import os

USE_CUDA = torch.cuda.is_available()
DEVICE = torch.device(&#39;cuda&#39; if USE_CUDA else &#39;cpu&#39;)
BATCH_SIZE = 256
EPOCH = 30

DEVICE</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>device(type=&#39;cuda&#39;)</p>
<p>잘 설정되었다면 위와 같이 cuda가  출력될 것이다.</p>
<h3 id="전처리">전처리</h3>
<p>다음으로는 <code>torch</code>에 맞게 데이터를 변형시켜주어야한다.</p>
<pre><code class="language-python">import torchvision.transforms as transforms
from torchvision.datasets import ImageFolder

# 사진의 파일이 제각각이므로 resize를 통해 64 * 64로 맞춰주고 tensor로 변환
transform_base = transforms.Compose([transforms.Resize((64, 64 )), transforms.ToTensor()])

# ImageFolder: 폴더 구조로 이루어진 데이터셋을 처리하기 위한 클래스
train_dataset = ImageFolder(root=&#39;./splitted/train&#39;, transform=transform_base)
val_dataset = ImageFolder(root=&#39;./splitted/val&#39;, transform=transform_base)

# 배치로 쪼개기
from torch.utils.data import DataLoader

train_loader = torch.utils.data.DataLoader(train_dataset,
                                           batch_size=BATCH_SIZE,
                                           shuffle=True,
                                           num_workers=4)

val_loader = torch.utils.data.DataLoader(val_dataset,
                                        batch_size=BATCH_SIZE,
                                        shuffle=True,
                                        num_workers=4)</code></pre>
<p>앞서 실습에서는 사진 사이즈가 모두 동일하여 resize 할 필요가 없었지만 이번 실습에서는 사진의 크기가 제각각이여서 64 * 64로 동일하게 맞춰주고 tensor로 변환해주는 작업을 해주었다.</p>
<p><code>ImageFolder</code> 클래스에 대해 잘 모르겠어서 GPT에게 물어보았더니 아래와 같이 답변해주었다.
<img src="https://velog.velcdn.com/images/castle_mi/post/d7c5eece-5505-4237-bc68-fcaedb4cd7bb/image.png" alt=""></p>
<p>그래도 잘...ㅎㅎㅎ 🤯🤯 </p>
<p>여튼 배치도 여러 개 나눠주고</p>
<pre><code class="language-python"># 배치로 쪼개기
from torch.utils.data import DataLoader

train_loader = torch.utils.data.DataLoader(train_dataset,
                                           batch_size=BATCH_SIZE,
                                           shuffle=True,
                                           num_workers=4)

val_loader = torch.utils.data.DataLoader(val_dataset,
                                        batch_size=BATCH_SIZE,
                                        shuffle=True,
                                        num_workers=4)</code></pre>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/5cb3124f-5cb6-444a-b5d9-ec7716e6f4d8/image.png" alt=""></p>
<p>첫번째 배치만 따로 떼어놓고 본 결과는 위와 같다.</p>
<p>저 <code>torch.Size([256, 3, 64, 64])</code>에 주의하여 이제 모델을 구성해주어야 한다.</p>
<br>

<h3 id="모델링">모델링</h3>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/2d01a4de-b8a2-4e1d-8b85-28e86d55b0b0/image.png" alt="">
일단 만들고자 하는 모델은 위와 같은 구성이다.
이를 코드로 구현하면 아래와 같다.</p>
<br>

<p><strong>📌 모델 구성 및 선언</strong></p>
<pre><code class="language-python"># 모델링
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        ## nn.Conv2D(입력받는 채널 수, 출력할 채널 수, 커널 사이즈, padding 옵션)
        self.conv1 = nn.Conv2d(3, 32, 3, padding=1)
        self.pool = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(32, 64, 3, padding=1)
        self.conv3 = nn.Conv2d(64, 64, 3, padding=1)
        self.fc1 = nn.Linear(4096, 512) # 8 * 8 * 64 = 4096
        self.fc2 = nn.Linear(512, 33)

    def forward(self, x):

        x = self.conv1(x) # (64, 64) 
        x = F.relu(x)
        x = self.pool(x) # (32, 32) 
        x = F.dropout(x, p=0.25, training=self.training) # train 모드에서만 dropout 사용

        x = self.conv2(x) # (32 , 32)
        x = F.relu(x)
        x = self.pool(x) # (16, 16)
        x = F.dropout(x, p=0.25, training=self.training)

        x = self.conv3(x) # (16, 16)
        x = F.relu(x)
        x = self.pool(x) # (8, 8)
        x = F.dropout(x, p=0.25, training=self.training)

        x = x.view(-1, 4096) # flatten()
        x = self.fc1(x)
        x = F.relu(x)
        x = F.dropout(x, p=0.5, training = self.training)
        x = self.fc2(x)

        return F.log_softmax(x, dim=1)

# 모델 선언    
model_base = Net().to(DEVICE)
optimizer = optim.Adam(model_base.parameters(), lr=0.001)</code></pre>
<br>

<p><strong>📌 학습 및 평가</strong></p>
<p>그리고 이제 각 배치 데이터에 대하여 학습을 시키고 평가를 해야한다. 하지만 이렇게 데이터가 방대한 경우 학습과 평가에 대한 코드를 함수로 만들어준 후 각 epoch 중 가장 좋은 성능을 지닌 모델을 저장하는 방향으로 코드를 짜는 것이 좋다.</p>
<p>** - 함수 생성**</p>
<pre><code class="language-python"># 학습
def train(model, train_loader, optimizer):
    model.train() # 모드 선언
    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.to(DEVICE), target.to(DEVICE)

        optimizer.zero_grad()
        output = model(data)
        loss = F.cross_entropy(output, target)

        loss.backward()
        optimizer.step()    

# 평가
def evaluate(model, test_loader):
    model.eval() # 모드 선언
    test_loss = 0
    correct = 0

    # with 자원을 열면 닫지 않아도 with 구문이 끝나면 알아서 닫아줌(error대응 잘함)
    with torch.no_grad(): # gradient가 없는동안 아래 동작을 실시하라
        for data, target in test_loader:
            data, target = data.to(DEVICE), target.to(DEVICE)
            output = model(data)

            test_loss += F.cross_entropy(output, target, reduction=&#39;sum&#39;).item()

            pred = output.max(1, keepdim=True)[1] # 최대값 위치의 클래스가 최종 클래스
            correct += pred.eq(target.view_as(pred)).sum().item()

    test_loss /= len(test_loader.dataset)
    test_accuracy = 100. * correct / len(test_loader.dataset)   # 맞는 개수
    return test_loss, test_accuracy</code></pre>
<br>

<p>** - 실제 실행 코드 **</p>
<pre><code class="language-python">import time
import copy 

def train_baseline(model, train_loader, val_loader, optimizer, num_epochs = 30):
    best_acc = 0.0 # 가장 좋은 acc를 저장할 변수
    best_model_wts = copy.deepcopy(model.state_dict()) # 가장 좋은 acc 모델의 weight 저장

    for epoch in range(1, num_epochs + 1):
        since = time.time()
        train(model, train_loader, optimizer) # 학습 
        train_loss, train_acc = evaluate(model, train_loader) # train에 대한 평가
        val_loss, val_acc = evaluate(model, val_loader) # val에 대한 평가

        if val_acc &gt; best_acc:  # 30번의 epoch 중 가장 val_accuracy가 좋은 weight를 저장
            best_model_wts = copy.deepcopy(model.state_dict())

        time_elapsed = time.time() - since
        print(&#39;----------------- epoch {} -----------------&#39;.format(epoch))
        print(&#39;train Loss: {:.4f}, Accuracy: {:.2f}%&#39;.format(train_loss, train_acc))
        print(&#39;val Loss: {:.4f}, Accuracy: {:.2f}%&#39;.format(val_loss, val_acc))
        print(&#39;Complieted in {:.0f}m {:.0f}s&#39;.format(time_elapsed // 60, time_elapsed % 60))
    model.load_state_dict(best_model_wts)
    return model

base = train_baseline(model_base, train_loader, val_loader, optimizer, EPOCH)
torch.save(base, &#39;baseline.pt&#39;) # 파이토치 파일로 가장 좋은 acc 모델 저장</code></pre>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/329d733c-4e38-4001-a462-ffea49050316/image.png" alt=""></th>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/3d32634a-48fc-481c-8e4e-fbed626bc409/image.png" alt=""></th>
</tr>
</thead>
</table>
<p>그러면 위와 같이 각 epoch에 대해 출력해주고 가장 좋은 acc을 가진 모델을 저장까지 해준다.</p>
<br>


<h2 id="전이학습">전이학습</h2>
<h3 id="이미지-증강augmentation">이미지 증강(augmentation)</h3>
<p>이미 학습이 잘 된 모델을 나에게 맞게 조금만 변형하여 사용하는 것을 전이학습이라 한다.</p>
<p>이때 weight까지 그대로 가져오는 경우도 있고 일부분은 구조만 가져와서 학습을 시켜 가중치를 매기는 경우도 있다.</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/b968616f-7d38-498a-bd23-674908687a55/image.png" alt=""></p>
<p>모델 전체를 가져올지, 일부분만 가져올지 등 여러 고민에 대한 생각은 대부분 위와 같은 사고를 통해 결정된다.</p>
<p>이 실습을 해보자.</p>
<p>이미지 데이터는 구하기 쉬운 데이터가 아니여서 가지고 있는 데이터를 최대한 활용하는 것이 중요하다. 또한 이미지 데이터 수가 충분하다고 하더라도 과적합을 방지하기 위해 일부러 이미지를 변형시키기도 한다.</p>
<p>데이터를 일부러 변형시켜서 그 수를 늘리는데,  무작위로 자르고 뒤집고 회전시키며 색조와 명도를 조정하는 등의 이미지 증강 작업을 수행한다.</p>
<pre><code class="language-python">data_transforms = {     # 과적합 방지용 -&gt; 돌리고, 상하좌우 반전, 이미지 자르기, 색상
    &#39;train&#39; : transforms.Compose([transforms.Resize([64, 64]),  
        transforms.RandomHorizontalFlip(), transforms.RandomVerticalFlip(), 
        transforms.RandomCrop(52), transforms.ToTensor(),    
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]),  

    &#39;val&#39; : transforms.Compose([transforms.Resize([64, 64]),
        transforms.RandomHorizontalFlip(), transforms.RandomVerticalFlip(),
        transforms.RandomCrop(52), transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]),
}

data_dir = &#39;./splitted&#39;

# 데이터 변형
image_datasets = {x: ImageFolder(root=os.path.join(data_dir, x), 
                                 transform=data_transforms[x]) for x in [&#39;train&#39;, &#39;val&#39;]}
# 배치 나누기 
dataloaders = {x: torch.utils.data.DataLoader(image_datasets[x], 
                                              batch_size = BATCH_SIZE,
                                              shuffle=True,
                                              num_workers=4) for x in [&#39;train&#39;, &#39;val&#39;]}
dataset_sizes = {x: len(image_datasets[x]) for x in [&#39;train&#39;, &#39;val&#39;]}

class_names = image_datasets[&#39;train&#39;].classes # 클래스명 담긴 변수</code></pre>
<br>

<h3 id="학습된-모델-불러오기">학습된 모델 불러오기</h3>
<p><code>from torchvision import models</code> 을 이용하면 여러 학습된 모델을 불러올 수 있다.</p>
<pre><code class="language-python">from torchvision import models

# resnet50
resnet = models.resnet50(pretrained=True)   
                        # True - 학습이 완료된 weight 가져옴 / False - 구조만 가져옴
&#39;&#39;&#39;
대부분 사전 학습된 모델과 우리의 데이터의 출력될 클래스의 숫자가 다를 것이다.
이를 생각하고 항상 마지막 레이어의 숫자를 변형해주어야한다!!! 
&#39;&#39;&#39;
num_ftrs = resnet.fc.in_features # in_features - 마지막 레이어 채널 숫자에 해당하는 것 
resnet.fc = nn.Linear(num_ftrs, len(class_names)) # len(class_names) : 33개로 수정 
resnet = resnet.to(DEVICE)

criterion = nn.CrossEntropyLoss()
&#39;&#39;&#39;
filter(lambda p: p.requires_grad, resnet.parameters())
사전 학습된 모델의 weight까지 가져오는 것이지만, 마지막 레이어가 바뀌었기 때문에
해당 weight는 다시 학습시킬 필요가 있다.
&#39;&#39;&#39;
optimizer_ft = optim.Adam(filter(lambda p: p.requires_grad, resnet.parameters()), lr=0.001)

from torch.optim import lr_scheduler    # epoch에 따라 running rate를 바꾸는 작업을 해줌
exp_lr_scheduler = lr_scheduler.StepLR(optimizer_ft, step_size=7, gamma=0.1)    
                  # 7 epoch마다 running rate를 0.1씩 감소 시킴</code></pre>
<p>중간 중간 주석처리된 부분을 이해해주어야한다.</p>
<br>

<h3 id="모델-수정-후-학습시키기">모델 수정 후 학습시키기</h3>
<p><strong>📌 학습시키지 않을 레이어는 고정</strong></p>
<pre><code class="language-python">ct = 0
for child in resnet.children(): # resnet 모델의 하위 레이어를 for문으로 추출 
    ct += 1
    if ct &lt; 6:  # 입력에 가까운 0-5번 레이어
        # 학습하지 않도록 고정
        for param in child.parameters():
            param.requires_grad = False
    # 따로 설정하지 않은 6번 ~ 레이어는 학습 시킴 </code></pre>
<br>

<p><strong>📌 나머지 레이어들에 대해 학습</strong></p>
<pre><code class="language-python"># 학습을 하면서 가장 acc가 좋은 모델 저장
def train_resnet(model, criterion, optimizer, scheduler, num_epochs=25):
  best_model_wts = copy.deepcopy(model.state_dict())
  best_acc = 0.0 # 가장 좋은 acc가 될 변수

  for epoch in range(num_epochs):
      print(&#39;----------------- epoch {} -----------------&#39;.format(epoch+1))
      since = time.time()
      for phase in [&#39;train&#39;, &#39;val&#39;]:
          # 모드 설정 
          if phase == &#39;train&#39;:
              model.train()
          else:
              model.eval()

          # 각 epoch를 시작할 때 마다 loss, corrects 초기
          running_loss = 0.0
          runnung_corrects = 0

          # 학습
          for inputs, labels in dataloaders[phase]:
              inputs = inputs.to(DEVICE)
              labels = labels.to(DEVICE)

              optimizer.zero_grad()

              # Train 데이터라면 gradients 업데이트를 허가 
              with torch.set_grad_enabled(phase == &#39;train&#39;):
                  outputs = model(inputs)
                  _, preds = torch.max(outputs, 1)
                  loss = criterion(outputs, labels)

                  if phase == &#39;train&#39;:
                      loss.backward()
                      optimizer.step()

              # loss 와 맞는 개수 계산 
              # inputs.size(0) : 배치 사이즈
              running_loss += loss.item() * inputs.size(0)
              runnung_corrects += torch.sum(preds == labels.data)
          if phase == &#39;train&#39;: # learning rate 업데이트
              scheduler.step()

          epoch_loss = running_loss/dataset_sizes[phase]
          epoch_acc = runnung_corrects.double()/dataset_sizes[phase]

          print(&#39;{} Loss: {:.4f} Acc: {:.4f}&#39;.format(phase, epoch_loss, epoch_acc))

          # acc가 이전보다 좋아졌다면 업데이트
          if phase == &#39;val&#39; and epoch_acc &gt; best_acc:
              best_acc = epoch_acc
              best_model_wts = copy.deepcopy(model.state_dict())

      time_elapsed = time.time() - since
      print(&#39;Complieted in {:.0f}m {:.0f}s&#39;.format(time_elapsed // 60, time_elapsed % 60))
  print(&#39;Best val Acc: {:.4f}&#39;.format(best_acc))

  model.load_state_dict(best_model_wts)

  return model</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/e9c39b15-3f58-4c81-8e97-69d4aeaea730/image.png" alt=""></p>
<br>

<h3 id="평가하기">평가하기</h3>
<pre><code class="language-python"># 전처리
# 과적합 방지용 transform 그대로 적용
transform_resnet = transforms.Compose([
        transforms.Resize([64, 64]),  
        transforms.RandomCrop(52), 
        transforms.ToTensor(),    
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ])

test_resNet = ImageFolder(root=&#39;./splitted/test&#39;, transform=transform_resnet)
test_loader_resNet = torch.utils.data.DataLoader(test_resNet, 
                                              batch_size = BATCH_SIZE,
                                              shuffle=True,
                                              num_workers=4)

# 저장된 모델 불러오기
resnet50 = torch.load(&#39;resnet50.pt&#39;)
resnet50.eval() # 평가 모드 선언 
test_loss, test_accuracy = evaluate(resnet50, test_loader_resNet)

print(&#39;ResNet test acc:  &#39;, test_accuracy)</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>ResNet test acc:   98.97358868444111</p>
<p>test 데이터에 대해 전처리를 해주고 저장된 모델을 불러와 평가를 해주면 위와 같은 accuracy를 얻을 수 있다.</p>
<p><br><br></p>
<h2 id="tensorflow_datasets을-통해-알아보는-전이학습과-미세조정">tensorflow_datasets을 통해 알아보는 전이학습과 미세조정</h2>
<pre><code class="language-python">from __future__ import absolute_import, division, print_function, unicode_literals
import tensorflow as tf
import os
import numpy as np
import matplotlib.pyplot as plt

keras = tf.keras

import tensorflow_datasets as tfds
tfds.disable_progress_bar()</code></pre>
<h3 id="전이학습-1">전이학습</h3>
<p><strong>📌 데이터 준비</strong></p>
<pre><code class="language-python">(raw_train, raw_validation, raw_test), metadata = tfds.load(
    &#39;cats_vs_dogs&#39;,
    # 데이터를 인덱스로 분리하여 각각 train, val, test 데이터로 이용
    split = [&#39;train[:80%]&#39; , &#39;train[80%:90%]&#39;, &#39;train[90%:]&#39;],
    with_info = True,
    as_supervised = True # 데이터가 라벨과 함께 튜플 형태로 저장
)</code></pre>
<p><code>tensorflow_datasets</code>에 있는 <code>cats_vs_dogs</code> 데이터를 불러와주었다.</p>
<p><code>tfds.load</code>의 <code>split</code>옵션을 이용하면 불러올 때 각 인덱스를 기준으로 데이터를 분리해서 가져올 수 있다.</p>
<p><strong>📌 데이터 확인</strong></p>
<pre><code class="language-python"># 2개
get_label_name = metadata.features[&#39;label&#39;].int2str

for image, label in raw_train.take(2):
  plt.figure()
  plt.imshow(image)
  plt.title(get_label_name(label))</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/6d0bfd43-3f02-4faa-b5c8-d1e292cdc171/image.png" alt=""></p>
<p>metadata의 label을 <code>get_label_name</code> 변수에 저장해두고 <code>.take()</code>를 통해 2개만 가져오게 시켜 그림과 라벨을 같이 뽑아낼 수도 있다.</p>
<br>

<h4 id="전처리-1">전처리</h4>
<p>이미 학습된 모델을 가지고 오는 것이므로 해당 모델에 맞게 다양한 전처리를 해줄 필요가 있다. </p>
<p>아래 코드는 이미지 사이즈를 160 * 160 으로 resize해주고 -1과 1 사이의 값을 가지도록 scale을 해준 작업이다.</p>
<pre><code class="language-python">IMG_SIZE = 160

def format_example(image, label):
  image = tf.cast(image, tf.float32)
  image = (image / 127.5) - 1 # scale
        # -1과 1 사이의 값을 가지도록 0과 255의 중간값으로 나눠주고 1 빼줌
  image = tf.image.resize(image, (IMG_SIZE, IMG_SIZE))
  return image, label


# map 함수를 이용해 빠르게 적용
train = raw_train.map(format_example)
validation = raw_validation.map(format_example)
test = raw_test.map(format_example)</code></pre>
<p>또한 배치를 만들고 섞어주었다.</p>
<pre><code class="language-python"># 배치 만들고 shuffle
BATCH_SIZE = 32
SHUFFLE_BUFFER_SIZE = 1000

train_batches = train.shuffle(SHUFFLE_BUFFER_SIZE).batch(BATCH_SIZE)
validation_batches = validation.batch(BATCH_SIZE)
test_batches = test.batch(BATCH_SIZE)

# 확인
for image_batch, label_batch in train_batches.take(1):
  pass
image_batch.shape # 적용한 이미지 사이즈와 배치 사이즈가 적용됨</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>TensorShape([32, 160, 160, 3])</p>
<p>첫 번째 배치에 대해서만 확인하면 되므로 바로 <code>break</code>를 걸어주었다.</p>
<br>

<h4 id="mobilenet-v2-모델">MobileNet V2 모델</h4>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/a3112074-561d-43b7-b774-f8ae03027caf/image.png" alt=""></p>
<p><code>MobileNet V2</code> 모델은 위와 같은 구조를 가진다. 그리고 아래와 같은 코드로 학습된 모델을 불러올 수 있다.</p>
<pre><code class="language-python"># 사전 훈련된 MobileNetV2
IMG_SHAPE = (IMG_SIZE, IMG_SIZE, 3)

base_model = tf.keras.applications.MobileNetV2(input_shape = IMG_SHAPE,
                                               include_top = False, 
                                               weights = &#39;imagenet&#39;)</code></pre>
<p>이때 몇 가지 설정을 해주어야한다.</p>
<ul>
<li><code>include_top = False</code> 
  : 모델의 맨 위층(맨 마지막 레이어)에는 사전 훈련된 데이터의 원 핫 인코딩이 되어있는 상태일 것이다. 이를 우리 데이터로 바꿔주어야하므로 맨 위층을 빼고 가져오도록 설정</li>
<li><code>weights = &#39;imagenet&#39;</code>
  : &#39;imagenet&#39;으로 학습된 모델을 불러오도록 설정</li>
</ul>
<pre><code class="language-python"># (160 , 160, 3 )-&gt; (5, 5, 1280)
feature_batch = base_model(image_batch)
feature_batch.shape # 1280 : 채널 수</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>TensorShape([32, 5, 5, 1280])</p>
<p>이렇게 불러온 모델을 확인해보면 (160 , 160, 3 )-&gt; (5, 5, 1280) 변환된 것을 확인할 수 있다.</p>
<p>이때 1280이 채널 수이다.</p>
<br>

<h4 id="📌-가중치를-그대로-사용"><strong>📌 가중치를 그대로 사용</strong></h4>
<p>먼저 학습된 모델의 가중치를 그대로 사용해보자.</p>
<pre><code class="language-python"># 가중치를 그대로 사용하기 위함
base_model.trainable = False</code></pre>
<p>위와 같이 설정해두고 <code>base_model.summary()</code>를 실행해주면 아래와 같이 Trainable params의 수가 0이 뜰 것이다.
<img src="https://velog.velcdn.com/images/castle_mi/post/19b4e127-ef52-4d86-a7db-f3d56c1ea5a3/image.png" alt=""></p>
<br>

<p>학습된 모델의 가중치는 그대로 사용한다고 했지만,
우리는 한 가지 작업을 더 해주어야한다.</p>
<p><strong>🌟 바로 마지막 레이어를 우리 데이터에 맞게 추가해주어야하는 것!</strong></p>
<p>지금은 마지막 레이어를 <code>GlobalAveragePooling2D층</code> 과 <code>Dense 층</code>을 이용해 쌓아주었다.</p>
<pre><code class="language-python"># GlobalAveragePooling2D층
# 채널 마다의 평균값을 이용
global_average_layer = tf.keras.layers.GlobalAveragePooling2D()
feature_batch_average = global_average_layer(feature_batch)
# feature_batch_average.shape # TensorShape([32, 1280])

# Dense 층
prediction_layer = keras.layers.Dense(1)
prediction_batch = prediction_layer(feature_batch_average)
# prediction_batch.shape # TensorShape([32, 1])</code></pre>
<br>

<p><strong>📌 최종 전체 모델 구성</strong></p>
<pre><code class="language-python"># 전체 모델 구성
model = tf.keras.Sequential([
    base_model, # 기존 mobilenet
    # 우리가 추가한 레이어
    global_average_layer,
    prediction_layer
])

# 컴파일
base_learning_rate = 0.0001
model.compile(optimizer = tf.keras.optimizers.RMSprop(lr = base_learning_rate), 
              loss = tf.keras.losses.BinaryCrossentropy(from_logits = True),
              metrics = [&#39;accuracy&#39;])</code></pre>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/31be926d-422d-4817-94e9-2b1a2e706461/image.png" alt=""></p>
<p>추가할 마지막 레이어를 기존 <code>base_model</code>과 함께 쌓아주고 컴파일해주면 모델링은 끝났다. </p>
<p><em>- 개와 고양이를 분류하는 문제이므로 BinaryCrossentropy를 이용함.</em></p>
<p>학습을 시키지 않은 상태의 성능도 확인해볼 수 있다.
<img src="https://velog.velcdn.com/images/castle_mi/post/a58bbf97-04bf-4c90-8015-8f38f208ecb7/image.png" alt=""></p>
<p>학습을 시키지 않았으니 성능이 좋지 않은 것은 당연하다. </p>
<p>그렇다면 얼른 학습시킨 성능을 살펴보자.</p>
<p><strong>📌 학습 및 평가</strong></p>
<pre><code class="language-python"># 학습
history = model.fit(train_batches,
                    epochs = initial_epochs,
                    validation_data = validation_batches)
# 전체 연산을 다 해야하므로 시간이 오래 걸리지만 
# mobilenet을 다시 학습하는 것보단 훨씬 빠름

# 평가
acc = history.history[&#39;accuracy&#39;]
val_acc = history.history[&#39;val_accuracy&#39;]

loss = history.history[&#39;loss&#39;]
val_loss = history.history[&#39;val_loss&#39;]

plt.figure(figsize = (8, 8))
plt.subplot(2, 1, 1)
plt.plot(acc, label = &#39;Training Acc&#39;)
plt.plot(val_acc, label = &#39;Val Acc&#39;)
plt.legend(loc = &#39;lower right&#39;)
plt.ylabel(&#39;Acc&#39;)
plt.ylim([min(plt.ylim()), 1])
plt.title(&#39;Training and Val Acc&#39;)

plt.subplot(2, 1, 2)
plt.plot(loss, label = &#39;Training Loss&#39;)
plt.plot(val_loss, label = &#39;Val Loss&#39;)
plt.legend(loc = &#39;upper right&#39;)
plt.ylabel(&#39;Cross Entropy&#39;)
plt.ylim([0,1.0])
plt.title(&#39;Training and Val Loss&#39;)
plt.xlabel(&#39;epoch&#39;)
plt.show()
</code></pre>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/cb4f7bec-ce4d-4011-9bc8-5bd8dce0c80c/image.png" alt=""></th>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/5a074f7b-adb7-43e1-b615-e0874c32a5e3/image.png" alt=""></th>
</tr>
</thead>
</table>
<p>loss는 점점 떨어지고 있고 acc는 점점 상승하고 있다. </p>
<p>이것만으로도 좋지만, 
이번에는 가중치 중 일부만 학습된 모델의 가중치를 사용하고 일부는 튜닝을 해보도록 설정해보자.</p>
<h4 id="📌-미세-조정"><strong>📌 미세 조정</strong></h4>
<p>일단 먼저 모든 pram에 대해 trainable하게 변경해주어야한다.</p>
<pre><code class="language-python"># 모두 trainable하게 변경
base_model.trainable = True
print(&#39;Number of layers in the base model : &#39;, len(base_model.layers))</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>Number of layers in the base model :  154</p>
<p>우리가 튜닝할 수 있는 총 레이어의 개수가 154개라는 것을 확인할 수 있다.</p>
<p>모든 층을 튜닝하기 보단, 100번째의 레이어부터 튜닝 가능하도록 설정해줄 것이다.</p>
<pre><code class="language-python"># 100번째 층부터 튜닝 가능하게 설정
fine_tune_at = 100

# fine_tune_at 층 이전의 모든 층 고정
for layer in base_model.layers[:fine_tune_at]:
  layer.trainable = False

# 학습 비율 낮춤
model.compile(optimizer = tf.keras.optimizers.RMSprop(lr = base_learning_rate), 
              loss = tf.keras.losses.BinaryCrossentropy(from_logits = True),
              metrics = [&#39;accuracy&#39;])
model.summary()</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/e59a9f4b-04e6-4ec1-b95d-d3f96860f490/image.png" alt=""></p>
<p>위와 같이 설정해주면 trainable param이 0이었던 이전과 달리 1862721개를 튜닝할 수 있게 되었다.</p>
<br>

<p>그리고 이제 epoch을 돌며 학습을 시켜주어야하는데 
<img src="https://velog.velcdn.com/images/castle_mi/post/8291bd06-ec6b-43af-82fe-3405ee85bc2d/image.png" alt="">
앞서 학습시켰던 모델에 이어서 학습할 수 있도록 설정해줄 수도 있다.</p>
<pre><code class="language-python"># 20번의 epoch
fine_tune_epochs = 10
total_epochs = initial_epochs + fine_tune_epochs # 20

history_fine = model.fit(train_batches,
                         epochs = total_epochs,
                         # 이전 모델의 epoch부터 학습을 시작하게 해서
                         # 10 epoch부터 이어서 학습하게 함
                         initial_epoch = history.epoch[-1],
                         validation_data = validation_batches)</code></pre>
<p>이전 history의 <code>history.epoch[-1]</code>을 추출하여 처음 시작하는 epoch 지점을 잡아주고 10 epoch을 더 돌게 해주면 아래와 같이 10 epoch부터 시작하여 20 epoch까지 도는 것을 확인할 수 있다.
<img src="https://velog.velcdn.com/images/castle_mi/post/2bbf7a54-8eee-4cc2-ba72-4cf36548936d/image.png" alt=""></p>
<br>

<p><strong>📌 평가</strong></p>
<pre><code class="language-python"># 최초 history에 방금 학습 결과 추가
acc += history_fine.history[&#39;accuracy&#39;]
val_acc += history_fine.history[&#39;val_accuracy&#39;]

loss += history_fine.history[&#39;loss&#39;]
val_loss += history_fine.history[&#39;val_loss&#39;]</code></pre>
<p>그리고 이전의 학습 결과에 성능을 추가하고
튜닝을 시작하게 된 시점부터 그래프를 더 추가하여 그리면 아래와 같다.</p>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/9bd29f1f-5452-4b0d-8210-48e95e62fbb9/image.png" alt=""></th>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/b2f3f8bb-3743-4bc5-9ef1-c7adb951d3db/image.png" alt=""></th>
</tr>
</thead>
</table>
<p>음... 원래는 loss는 계속 떨어지고 acc는 계속 높아지는 그래프를 원했는데 많이 변동성이 심해진 것 같다 😅😅 </p>
<p>여튼 이렇게 일부 가중치는 튜닝할 수도 있다는 사실을 기억하자.</p>
<p><br><br></p>
<h3 id="텐서플로-허브">텐서플로 허브</h3>
<p>텐서플로 허브에서도 사전 훈련된 모델을 가져오고 데이터도 사용할 수 있다.</p>
<p>이번에는 텐서플로 허브 공식 사이트의 코드를 따라하며 다른 이미지로도 실습해보자.
<a href="https://www.tensorflow.org/tutorials/images/transfer_learning_with_hub?hl=ko"><img src="https://velog.velcdn.com/images/castle_mi/post/ee3f83fc-0615-44df-b90c-c491c0b090f2/image.png" alt=""></a>
<em>공식 사이트 속 어디에 있는지 계속 찾았는데 이제야 찾았다... 🫠</em></p>
<p><strong>📌 Mobilenet V2 가져오기</strong></p>
<pre><code class="language-python">#pip install -U tf-hub-nightly
import tensorflow_hub as hub

# mobilenet 가져오기
classifier_url = &#39;https://tfhub.dev/google/tf2-preview/mobilenet_v2/classification/2&#39;

# 사전 훈련된 MobileNetV2
IMG_SHAPE = (224, 224)

classifier = tf.keras.Sequential(
    hub.KerasLayer(classifier_url, 
    # Mobilenet은 224, 224, 3(RGB) input 형태
    input_shape = IMG_SHAPE + (3,))
)
classifier.summary()</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/14b14b10-0d4b-427a-9c61-24425fb96ce0/image.png" alt=""></p>
<p>앞서 실습했던 <code>Mobilenet V2</code>를 텐서플로 허브에서 가져오려면 위와 같은 코드를 실행시키면 된다.</p>
<p>그리고 예측해볼 이미지를 하나 확인해보자.
원래는 데이터의 구성이 어떠한지 확인해보고 넘어가고 싶었는데 각 데이터가 의미하는 바가 무엇인지 찾지는 못했다. </p>
<p>예측해본 바로는 사진을 받아서 해당 이미지가 어떤 카테고리(ImageNetLabels.txt)에 속해있는지를 예측해보는 과정인 것 같다.</p>
<pre><code class="language-python"># 이미지 하나 확인
import PIL.Image as Image

url = &quot;https://storage.googleapis.com/download.tensorflow.org/example_images/grace_hopper.jpg&quot;
grace_hopper = tf.keras.utils.get_file(&#39;image.jpg&#39;, url)
grace_hopper = Image.open(grace_hopper).resize(IMG_SHAPE)
grace_hopper</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/39810453-8845-47da-9cc4-fda2c6819241/image.png" alt=""></p>
<br>

<p><strong>📌 학습 및 예측</strong></p>
<pre><code class="language-python"># 정규화 및 예측
grace_hopper = np.array(grace_hopper) / 255.0
print(grace_hopper.shape)

result = classifier.predict(grace_hopper[np.newaxis, ])

# argmax로 인덱스 찾기
predicted_class = np.argmax(result[0], axis = -1) # 653

# label을 받아서 해당 클래스의 라벨 값 추출
url = &quot;https://storage.googleapis.com/download.tensorflow.org/data/ImageNetLabels.txt&quot;
labels_path = tf.keras.utils.get_file(&#39;ImageNetLabels.txt&#39;, url)
imagenet_labels = np.array(open(labels_path).read().splitlines())

# 확인
plt.imshow(grace_hopper)
plt.axis(&#39;off&#39;)
predicted_class_name = imagenet_labels[predicted_class]
_ = plt.title(&quot;Prediction : &quot; + predicted_class_name.title())</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/a6001f5c-2c20-4206-abf7-68384add6f6b/image.png" alt=""></p>
<p>군복이라는 것을 잘 예측했다!</p>
<p><br><br></p>
<p>아래는 여러 꽃 사진에 대한 클래스를 예측하는 실습이다.</p>
<p><strong>📌 데이터 준비</strong></p>
<pre><code>url = &quot;https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz&quot;
data_root = tf.keras.utils.get_file(&#39;flower_photos&#39;, 
                                    url, 
                                    untar = True) #압축 풀기


# rescale 및 라벨 인식
image_generator = tf.keras.preprocessing.image.ImageDataGenerator(rescale = 1/255)
image_data = image_generator.flow_from_directory(str(data_root), target_size = IMG_SHAPE)</code></pre><br>

<p><strong>📌 배치 하나에 대한 예측 결과</strong></p>
<pre><code class="language-python"># 배치 생성
for image_batch, label_batch in image_data:
  print(&quot;Image batch shape : &quot;, image_batch.shape) # (32, 224, 224, 3)
  print(&quot;Label batch shape : &quot;, label_batch.shape)  # (32, 5)
  break

# 배치 하나에 대한 예측 결과
result_batch = classifier.predict(image_batch)

predicted_class_names = imagenet_labels[np.argmax(result_batch, axis = -1)]

# 확인
plt.figure(figsize = (10, 9))
plt.subplots_adjust(hspace = 0.5)
for n in range(30):
  plt.subplot(6, 5, n+1)
  plt.imshow(image_batch[n])
  plt.title(predicted_class_names[n])
  plt.axis(&#39;off&#39;)
_ = plt.suptitle(&#39;ImageNet prediction&#39;)</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/c7b08627-86b3-4c4f-949c-566ade1890cc/image.png" alt=""></p>
<p>은근 틀린 것들도 보인다.</p>
<br>

<p><strong>📌 모델 생성</strong></p>
<p>이번에는 특성추출기를 가져오고 dense 레이어 하나를 붙인 모델을 만들어보자.</p>
<pre><code class="language-python"># 특징 추출기 가져오기
feature_extractor_url = &#39;https://tfhub.dev/google/tf2-preview/mobilenet_v2/feature_vector/2&#39;

feature_extractor_layer = hub.KerasLayer(feature_extractor_url,
                                         input_shape = (224, 224, 3))

feature_batch = feature_extractor_layer(image_batch)

# dense 레이어 추가
from tensorflow.keras import layers

feature_extractor_layer.trainable = False

model = tf.keras.Sequential([
    feature_extractor_layer,
              # 마지막 라벨은 나의 데이터 클래스에 맞춰서
    layers.Dense(image_data.num_classes, activation = &#39;softmax&#39;)
])

# 마지막 레이어
predictions = model(image_batch)

# 컴파일
model.compile(
    optimizer = tf.keras.optimizers.Adam(),
    loss = &#39;categorical_crossentropy&#39;,
    metrics = [&#39;acc&#39;]
)
model.summary()</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/8424d7ef-a5b1-4d10-ad2d-51b20f3120a3/image.png" alt=""></p>
<br>

<p><strong>📌 배치별 loss와 acc을 반환해주는 함수</strong></p>
<pre><code class="language-python"># callback 정의
class CollectBatchStats(tf.keras.callbacks.Callback):
  # loss와 acc를 배치별로 출력해줌
  def __init__(self):
    self.batch_losses = []
    self.batch_acc = []

  def on_train_batch_end(self, batch, logs = None):
    self.batch_losses.append(logs[&#39;loss&#39;])
    self.batch_acc.append(logs[&#39;acc&#39;])
    self.model.reset_metrics()</code></pre>
<br>

<p><strong>📌 학습 및 성능 평가</strong></p>
<pre><code class="language-python"># 학습
steps_per_epoch = np.ceil(image_data.samples/image_data.batch_size)

batch_stats_callback = CollectBatchStats()

history = model.fit_generator(image_data, epochs = 2, 
                              steps_per_epoch = steps_per_epoch,
                              callbacks = [batch_stats_callback])

# class name 할당
class_names  = sorted(image_data.class_indices.items(), key = lambda pair:pair[1])
class_names = np.array([key.title() for key, value in class_names])

# 다시 예측
predicted_batch = model.predict(image_batch)
predicted_id = np.argmax(predicted_batch, axis = -1)
predicted_label_batch = class_names[predicted_id]

label_id = np.argmax(label_batch, axis = -1)</code></pre>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/9c05af79-3a2f-438f-9866-ae2bf9a8d694/image.png" alt=""></p>
<p>정답들은 초록 글씨로, 오답들은 빨간 글씨로 출력하게 했는데 대부분 정답을 맞춘 것 처럼 보여졌다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[딥러닝] Pytorch(Boston 집 값 예측_회귀, Breast Cancer_이중 분류, MNIST_다중 분류)  ]]></title>
            <link>https://velog.io/@castle_mi/%EB%94%A5%EB%9F%AC%EB%8B%9D-PytorchBoston-%EC%A7%91-%EA%B0%92-%EC%98%88%EC%B8%A1%ED%9A%8C%EA%B7%80-Breast-Cancer%EC%9D%B4%EC%A4%91-%EB%B6%84%EB%A5%98-MNIST%EB%8B%A4%EC%A4%91-%EB%B6%84%EB%A5%98</link>
            <guid>https://velog.io/@castle_mi/%EB%94%A5%EB%9F%AC%EB%8B%9D-PytorchBoston-%EC%A7%91-%EA%B0%92-%EC%98%88%EC%B8%A1%ED%9A%8C%EA%B7%80-Breast-Cancer%EC%9D%B4%EC%A4%91-%EB%B6%84%EB%A5%98-MNIST%EB%8B%A4%EC%A4%91-%EB%B6%84%EB%A5%98</guid>
            <pubDate>Thu, 02 Nov 2023 04:22:02 GMT</pubDate>
            <description><![CDATA[<p>✍🏻 1일 공부 이야기.</p>
<p><a href="https://github.com/castlemi99/ZeroBaseDataSchool/blob/main/Deep%20Learning/5.%20Basic%20of%20Pytorch.ipynb"><img src="https://velog.velcdn.com/images/castle_mi/post/bba9869a-b232-4c1e-8c1d-5a12e3ed6694/image.png" alt=""></a>
오늘 실습한 코드 내용은 위 깃허브에 업로드해두었습니다. 사진을 클릭하면 이동해요 !</p>
<p><br><br></p>
<h1 id="pytorch">Pytorch</h1>
<p><code>!pip3 install torch torchvision torchaudio</code></p>
<p><code>torch</code>는 numpy대신 tensor를 사용하여 딥러닝에 많이 쓰이고 있다. </p>
<p>예를들어 아래와 같은 계산을 numpy가 아닌 tensor로 할 수 있고 제일 좋은 점은 기울기 계산을 <code>.grad</code>하나로 해결할 수 있다는 것이다.</p>
<pre><code class="language-python">import torch

# 일반적인 코드
x = 3.5
y = x*x + 2

# 토치에서
x = torch.tensor(3.5)
# 기울기를 계산할 수 있는 옵션 : requires_grad
x = torch.tensor(3.5, requires_grad=True)

# 만약 어떤 함수에서 x가 3.5일 때의 함수값을 찾고 싶다면
y = (x-1)*(x-2)*(x-3) # tensor(1.8750, grad_fn=&lt;MulBackward0&gt;)

# x값에서의 기울기 계산
y.backward()
x.grad</code></pre>
<p><br><br></p>
<p><code>pytroch</code>를 활용해 다양한 실습을 해보자.</p>
<h2 id="보스턴-집-값-예측회귀">보스턴 집 값 예측(회귀)</h2>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/f25a2705-f7ff-43d4-90b1-e02b9ebc1250/image.png" alt=""></p>
<p>현재 버전에서 <code>load_boston</code>은 사라졌다. 
그러므로 아래와 같이 데이터를 받거나 아니면 csv 파일을 다운받아 사용하면 된다.</p>
<br>

<p><strong>📌 데이터 준비</strong></p>
<pre><code class="language-python">from sklearn import datasets
X, y = datasets.fetch_openml(&#39;boston&#39;, return_X_y=True)</code></pre>
<h3 id="eda">EDA</h3>
<p><code>torch</code>에서 사용할 수 있도록 위 데이터를 변환시켜주어야한다.</p>
<pre><code class="language-python">import torch
import torch.nn as nn 
import torch.nn.functional as F 
import torch.optim as optim 

# 필요한 특성 선택
cols = [&#39;INDUS&#39;, &#39;RM&#39;, &quot;LSTAT&quot;, &quot;NOX&quot;, &quot;DIS&quot;]
# numpy배열을 torch 텐서로 변환하고 float으로 변환
data_x = torch.from_numpy(X[cols].values).float()
data_y = torch.from_numpy(y.values).float()

data_x.shape # torch.Size([506, 5])

# 변수 정리
X = data_x
y = data_y.reshape(len(y), 1)
print(X.shape, y.shape) # torch.Size([506, 5]) torch.Size([506, 1])</code></pre>
<p>필요한 컬럼들을 선택하고 numpy 배열을 tensor로 변환시켜준 후, 변수명을 정리해주었다.</p>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/8a564a2d-288e-47b4-91ed-4b54bb623b3a/image.png" alt=""> 데이터프레임</th>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/b17f171e-097f-4144-b7cb-a7d2b1bba811/image.png" alt=""> tensor</th>
</tr>
</thead>
</table>
<p><br><br></p>
<h3 id="모델링">모델링</h3>
<p><strong>📌 하이퍼파라미터 설정</strong></p>
<pre><code class="language-python"># 하이퍼파라미터
n_epochs = 2000
learning_rate = 1e-3
print_interval = 100</code></pre>
<p><strong>📌 모델 구성</strong>
<a href="https://pytorch.org/docs/stable/generated/torch.nn.Linear.html"><img src="https://velog.velcdn.com/images/castle_mi/post/eb55312f-9c18-4ce6-83d3-c59d9cefdd01/image.png" alt=""></a></p>
<p>회귀 예측이므로 <code>linear</code> 모델을 한 번 사용해보자.</p>
<pre><code class="language-python"># 모델링
# nn.Linear(X 사이즈, y 사이즈, bias)
model = nn.Linear(X.size(-1), y.size(-1))

# optimizer
optimizer = optim.SGD(model.parameters(), lr = learning_rate)</code></pre>
<p><code>X.size(-1)</code>는 X.size의 마지막 dim을 반환해준다. 이때 size는 shape과 동일한 기능을 한다.</p>
<p>위와 같이 모델을 구성하고 optimizer 설정까지 다 끝났다면 학습을 시켜주자.</p>
<br>

<p><strong>📌 학습</strong></p>
<pre><code># 학습
for i in range(n_epochs):
    y_hat = model(X) # 예측값
    loss = F.mse_loss(y_hat, y) # loss 계산

    optimizer.zero_grad() # optimizer의 gradient 초기화
    # 각 학습 단계마다의 gradient값 그 자체를 가지기 위해 
    # 이전 학습 단계에서 사용했던 gradient는 초기화해주어야함
    loss.backward() # 모델의 파라미터에 대한 gradient 계산 -&gt; 역전파 계산 

    optimizer.step() # 계산된 gradient를 사용하여 파라미터(weight, bias) 업데이트

    if (i + 1) % print_interval == 0: # 100번째마다 loss 출력
        print(&#39;Epoch %d : loss = %.4e&#39; % (i + 1, loss))</code></pre><blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/ad2758e4-93ef-4c41-889b-237b6dfff4f1/image.png" alt=""></p>
<p><code>torch</code>에서 학습을 시키는 코드는 위와 같다. 
조금 복잡해보이지만.. 살펴보면 오히려 더 간단하다.</p>
<br>

<blockquote>
</blockquote>
<ol>
<li><code>y_hat</code> : 모델을 통해 예측값 계산</li>
<li><code>loss</code> : loss 계산(예측값 - 실제값)</li>
<li><code>.zero_grad()</code> : <code>Gradient</code> 초기화 (🌟🌟🌟🌟🌟)</li>
<li><code>.backward()</code> : 모델의 파라미터(weight, bias)에 대한 <code>Gradient</code> 계산 -&gt; <code>역전파</code> 계산</li>
<li><code>.step()</code> : 계산된 <code>Gradient</code>를 통해 파라미터(weight, bias) 업데이트</li>
<li>위 과정 반복</li>
</ol>
<p>이때 각 학습 단계마다의 gradient 값으로 파라미터를 업데이트해야하므로 이전 학습단계의 gradient를 초기화하고 다시 계산해야한다는 것이 제일 중요하다!!!</p>
<br>

<p><strong>📌 성능 확인</strong></p>
<pre><code class="language-python"># 결과 정리
df = pd.DataFrame(torch.cat([y, y_hat], dim = 1).detach_().numpy(),
                  columns = [&#39;y&#39;, &#39;y_hat&#39;])
sns.pairplot(df, height=5)
plt.show()</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/f953e77b-8eb8-4221-828a-a6a29322c7e7/image.png" alt=""></p>
<p>대부분 잘 예측한 것 같다! </p>
<p><br><br><br></p>
<h2 id="breast-cancer이진-분류">Breast Cancer(이진 분류)</h2>
<p>다음으로는 단일 분류 문제인 암 판정 데이터를 딥러닝으로 예측해보자.</p>
<p><strong>📌 데이터 준비</strong></p>
<pre><code class="language-python">from sklearn.datasets import load_breast_cancer
cancer = load_breast_cancer()

# 데이터 정리
df = pd.DataFrame(cancer.data, columns = cancer.feature_names)
df[&#39;class&#39;] = cancer.target

# 특정 컬럼 추출
cols = [&#39;mean radius&#39;, &#39;mean texture&#39;,&#39;mean smoothness&#39;, &#39;mean compactness&#39;,&#39;mean concave points&#39;,
        &#39;worst radius&#39;, &#39;worst texture&#39;,&#39;worst smoothness&#39;, &#39;worst compactness&#39;,&#39;worst concave points&#39;, 
        &#39;class&#39;]

# torch화
data = torch.from_numpy(df[cols].values).float()

# 데이터 분리
X = data[:, :-1]
y = data[:, -1:]
X.shape, y.shape # (torch.Size([569, 10]), torch.Size([569, 1]))</code></pre>
<br>


<h3 id="모델링-1">모델링</h3>
<p>이번에는 이진 분류 문제이므로 linear 모델을 통과시키고 마지막에 <code>sigmoid</code>함수를 통과시켜 output이 2개만 나오도록 해주었다.</p>
<p>그리고 이번에는 모델을 클래스로 정의해보았다.</p>
<br>

<p><strong>📌 모델 구성</strong></p>
<pre><code class="language-python"># 하이퍼파라미터
n_epochs = 200000
learning_rate = 1e-2
print_interval = 10000

# 모델링(Pytorch 모델을 구현하기 위한 클래스)
class MyModel(nn.Module): # nn.Module을 상속받는 클래스 정의

    # 모델의 초기화
    def __init__(self, input_dim, output_dim):
        self.input_dim = input_dim
        self.output_dim = output_dim

        # super 설정을 해주어야지만 nn.Module의 속성을 상속받음
        super().__init__()

        # 모델 설정
        self.linear = nn.Linear(input_dim, output_dim) # 선형 모델
        self.act = nn.Sigmoid() # output은 sigmoid(데이터가 이진분류이니깐)

    # 순방향 연산
    def forward(self, x):
        y = self.act(self.linear(x))

        return y



# 모델 선언 및 loss, optimizer 선언
model = MyModel(input_dim = X.size(-1), output_dim = y.size(-1))

crit = nn.BCELoss() # loss function : Binary Cross Entropy

optimizer = optim.SGD(model.parameters(), lr = learning_rate)</code></pre>
<br>

<p><strong>📌 학습 및 성능 평가</strong></p>
<pre><code class="language-python"># 학습
for i in range(n_epochs):
    y_hat = model(X)
    loss = crit(y_hat, y)

    optimizer.zero_grad()
    loss.backward()

    optimizer.step()

    if (i + 1) % print_interval == 0: # 100번째마다 loss 출력
        print(&#39;Epoch %d : loss = %.4e&#39; % (i + 1, loss))


# 성능 평가
correct_cnt = (y == (y_hat &gt; .5)).sum()
total_cnt = float(y.size(0))

print(&#39;Accuracy : %.4f&#39; % (correct_cnt / total_cnt))</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>Accuracy : 0.9649</p>
<p>이진 분류의 값을 갖도록 0.5 이상인 값은 1로 판단하도록 한 후 성능을 평가해본 결과 96.4%의 준수한 성능을 보여주었다.</p>
<p><br><br><br></p>
<h2 id="mnist다중-분류">MNIST(다중 분류)</h2>
<h3 id="cudagpu-설정">cuda(GPU 설정)</h3>
<p>계산량이 많아지면 cpu보단 gpu에서 학습시키는 것이 계산 속도가 빨라진다. 
아래와 같은 코드를 실행시켜 gpu를 사용할 수 있다면 <code>cuda</code>가 출력되는데 현재 내가 사용하고 있는 노트북에선 gpu 설정이 되어있지 않아서 cpu로 작동시켰다.</p>
<pre><code class="language-python"># cuda 환경 설정
is_cuda = torch.cuda.is_available()
device = torch.device(&#39;cuda&#39; if is_cuda else &#39;cpu&#39;)

print(&#39;Current cuda device is &#39;, device)</code></pre>
<p>아마 colab에서 [런타임 유형]을 gpu로 설정하고 해당 코드를 실행시키면 cuda 환경으로 작동시킬 수 있을 것이다!</p>
<br>

<p>이제 본격적으로 데이터를 준비하고 torch로 MNIST를 학습시켜보자.</p>
<p><strong>📌 데이터 준비</strong></p>
<pre><code class="language-python"># 데이터 준비
# datasets 모듈에 있는 MNIST 데이터를 바로 다운받고 
# tensor로 까지 변환까지 해줌
train_data = datasets.MNIST(root = &#39;./data&#39;, train = True, 
                            download=True, transform=transforms.ToTensor())
test_data = datasets.MNIST(root = &#39;./data&#39;, train = False, 
                           transform=transforms.ToTensor())

print(&#39;Number of training data : &#39; , len(train_data))
print(&#39;Number of test data : &#39; , len(test_data))</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>Number of training data :  60000
Number of test data :  10000</p>
<p>위 코드는 datasets 모듈에서 제공하는 MNIST 데이터를 바로 다운받아 사용할 수 있는 코드이다. 
<code>transform</code> 옵션 설정을 하면 다운 받을 때 tensor로 변환까지 한 번에 할 수 있어 아주 유용하다!! (MNIST 데이터 받는 방법이 참 많은 것 같다 🫠)</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/a84741ce-a22e-4feb-ad8b-54475d84a4e2/image.png" alt=""></p>
<p>데이터를 하나 랜덤으로 추출해보았는데 <code>train_data[193][0].shape</code> 을 통해 <code>torch.Size([1, 28, 28])</code> 사이즈임을 알 수 있었고
출력된 결과를 보아 앞에는 픽셀 데이터, 마지막은 라벨 데이터로 보인다.</p>
<p>이 데이터를 직접 보려면 아래와 같은 코드를 실행시키면 된다.</p>
<pre><code class="language-python"># 데이터 보기
image, label = train_data[193]

# sqeeze() : dim이 1인 것을 삭제시킴
plt.imshow(image.squeeze().numpy(), cmap = &#39;gray&#39;)
plt.title(&#39;label : %s&#39; %label)
plt.show()</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/f1d7efbf-1c78-45eb-ae5d-3d98383c6ffd/image.png" alt=""></p>
<p>중요하게 보고 넘어가야할 점은 <code>torch</code> 에서 shape이 <code>torch.Size([1, 28, 28])</code> 이렇게 나온 것과 연관이 있다.</p>
<p><code>tensor</code>에서는 채널에 해당하는 dim이 맨 앞에 위치해있고 <code>numpy</code>에서는 채널에 해당하는 dim이 맨 마지막에 위치해있으므로 두 모듈을 번갈아 사용할 때 꼭 dim을 생각하면서 코드를 짜주어야한다!!!</p>
<p>그래서 위 그림을 출력할 땐 <code>sqeeze()</code>를 이용해 dim이 1에 해당하는 값은 삭제시키고 numpy로 변환시켜 <code>imshow</code>를 통과시켰다.</p>
<br>

<h3 id="모델링-2">모델링</h3>
<p>이번에는 앞선 과정과는 좀 다르게 배치를 나누어볼까 한다.</p>
<p>배치란, 데이터를 쪼개서 학습시킴으로써 메모리 사용량을 감소시키고 학습 속도를 향상시키는 등 다양한 이점을 가지게 해주는 것이다.</p>
<br>

<p><strong>📌 미니 배치 구성</strong></p>
<pre><code class="language-python"># 미니 배치 구성
batch_size = 50
learning_rate = 0.0001
epoch_num = 15

train_loader = torch.utils.data.DataLoader(dataset = train_data, 
                                           batch_size = batch_size,
                                           shuffle = True)
test_loader = torch.utils.data.DataLoader(dataset = test_data, 
                                           batch_size = batch_size,
                                           shuffle = True)
first_batch = train_loader.__iter__().__next__() # 첫번째 배치만 가져옴</code></pre>
<p><br><br></p>
<p><strong>📌 모델 구성</strong>
<img src="https://velog.velcdn.com/images/castle_mi/post/3c996c8d-6bcc-47f2-93de-9a9d14651b48/image.png" alt="">
위와 같은 구조를 가진 CNN 모델을 클래스로 정의하면 아래와 같다.</p>
<pre><code class="language-python"># 모델링
class CNN(nn.Module): # nn.Module을 상속받는 클래스 정의

    # 모델의 초기화
    def __init__(self):
        super(CNN, self).__init__()
        ## nn.Conv2D(입력받는 채널 수, 출력할 채널 수, 커널 사이즈, stride 수, 제로패딩 옵션)
        self.conv1 = nn.Conv2d(1, 32, 3, 1, padding = &#39;same&#39;)
        self.conv2 = nn.Conv2d(32, 64, 3, 1, padding = &#39;same&#39;)
        self.dropout = nn.Dropout2d(0.25)
        self.fc1 = nn.Linear(3136, 1000) # 7*7*64 = 3136
        self.fc2 = nn.Linear(1000, 10)

    # 순방향 연산
    def forward(self, x):
        x = self.conv1(x) # (28, 28)
        x = F.relu(x)
        x = F.max_pool2d(x, 2) # (14, 14)

        x = self.conv2(x) # (14, 14)
        x = F.relu(x)
        x = F.max_pool2d(x, 2) # (7, 7)

        x = self.dropout(x)
        x = torch.flatten(x, 1)

        x = self.fc1(x)
        x = F.relu(x)

        x = self.fc2(x)
        output = F.log_softmax(x, dim = 1)
        return output


# 선언
model = CNN().to(device)
optimizer = optim.Adam(model.parameters(), lr = learning_rate)
criterion = nn.CrossEntropyLoss()</code></pre>
<br>

<p><strong>📌 학습 및 성능 평가</strong></p>
<pre><code class="language-python"># 학습
model.train() # 학습 모드 선언

i = 1 # 실제 학습
for epoch in range(epoch_num):
    for data, target in train_loader: # 배치 단위로 나눈 데이터
        data = data.to(device)
        target = target.to(device)

        optimizer.zero_grad()
        output = model(data)
        loss = criterion(output, target)

        loss.backward()

        optimizer.step()

        if i % 1000 == 0: # 100번째마다 loss 출력
            print(&#39;Train Step : {}\tLoss: {:.3f}&#39;.format(i, loss.item()))
        i += 1


# 성능 평가
model.eval() # 평가 모드 선언 (dropout 기능 꺼짐)

correct = 0
for data, target in test_loader:
    data = data.to(device)
    target = target.to(device)
    output = model(data)
    prediction = output.data.max(1)[1] # argmax 기능. max값을 가지는 인덱스를 추출
    correct += prediction.eq(target.data).sum() # 정답지 개수를 계속 증가시켜줌

print(&#39;Test set Accuracy : {:.2f}%&#39;.format(100. * correct / len(test_loader.dataset)))</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>Test set Accuracy : 99.20%</p>
<p>학습(<code>model.train()</code>)과 성능 평가(<code>model.eval()</code>)를 할 때 모드를 선언할 수 있다.</p>
<p>모드를 선언해주면 아래와 같은 기능을 얻을 수 있다고 한다.
<img src="https://velog.velcdn.com/images/castle_mi/post/77f2f34f-00cc-4b88-9516-10a7518694b2/image.png" alt=""></p>
<p>CNN으로 MNIST를 학습시켰더니... 무려 정확도가 99%로 라니 😚😚 정말 대단한 것 같다 ㅎㅎ</p>
<p><br><br></p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[[딥러닝] LeNet으로 찾아낸 마스크 착용 여부(Kaggle)]]></title>
            <link>https://velog.io/@castle_mi/%EB%94%A5%EB%9F%AC%EB%8B%9D-LeNet%EC%9C%BC%EB%A1%9C-%EC%B0%BE%EC%95%84%EB%82%B8-%EB%A7%88%EC%8A%A4%ED%81%AC-%EC%B0%A9%EC%9A%A9-%EC%97%AC%EB%B6%80Kaggle</link>
            <guid>https://velog.io/@castle_mi/%EB%94%A5%EB%9F%AC%EB%8B%9D-LeNet%EC%9C%BC%EB%A1%9C-%EC%B0%BE%EC%95%84%EB%82%B8-%EB%A7%88%EC%8A%A4%ED%81%AC-%EC%B0%A9%EC%9A%A9-%EC%97%AC%EB%B6%80Kaggle</guid>
            <pubDate>Wed, 01 Nov 2023 05:12:58 GMT</pubDate>
            <description><![CDATA[<p>✍🏻 31일 공부 이야기.</p>
<p><a href="https://github.com/castlemi99/ZeroBaseDataSchool/blob/main/Deep%20Learning/3.%20Mask%20man%20classification.ipynb"><img src="https://velog.velcdn.com/images/castle_mi/post/01cae895-1c5b-4224-8b6c-f60f72f727bd/image.png" alt=""></a></p>
<p>오늘 공부한 실습 코드는 위 깃허브 사진을 클릭하면 이동됩니다 :) </p>
<br>

<h2 id="mask-datakaggle">Mask Data(Kaggle)</h2>
<p><a href="https://www.kaggle.com/datasets/ashishjangra27/face-mask-12k-images-dataset"><img src="https://velog.velcdn.com/images/castle_mi/post/0ea514c1-f2f7-46ab-adb9-0109c22998b0/image.png" alt=""></a></p>
<p>이번에는 마스크를 쓴 사람과 쓰지 않은 사람들의 사진을 학습하여 마스크 착용 여부를 알아내는 실습을 해보자.</p>
<p>캐글에서 파일을 다운받아줬는데 파이썬에서도 압축파일을 풀어주는 라이브러리가 있다.</p>
<p><strong>📌 압축 파일 관리 툴</strong></p>
<pre><code class="language-python"># 압축파일 관리 툴
import zipfile

content_zip = zipfile.ZipFile(&#39;./data/archive.zip&#39;) # 압축 해제
content_zip.extractall(&#39;./data&#39;)

content_zip.close()


import os
os.listdir(&#39;./data/Face Mask Dataset/&#39;) # 폴더 내 파일명(디렉토리명) 확인 가능</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>[&#39;Test&#39;, &#39;Train&#39;, &#39;Validation&#39;]</p>
<p>나는 코드 파일이 있는 폴더에 <code>data</code>라는 폴더를 만들어주었고 그 안에 해당 zip 파일이 들어가있었다.</p>
<p>압축을 해제하고 해당 폴더 안에 어떤 것들이 들어가있는지 확인해보았더니 &#39;Test&#39;, &#39;Train&#39;, &#39;Validation&#39; 폴더가 더 있는 것을 확인할 수 있었다.
<img src="https://velog.velcdn.com/images/castle_mi/post/0c42c15f-3d21-4f83-97df-908ef9bb9305/image.png" alt=""></p>
<p><strong>📌 파일 정리</strong></p>
<p>이미지 파일로 가득한 것들을 이미지 경로, 마스크 착용 여부, 파일이 있는 폴더 이름 총 3가지 변수로 분리한 데이터프레임을 만들어보았다. (관리하기 편하게 😊 )</p>
<pre><code class="language-python"># 파일을 정리해보자.
path = &quot;./data/Face Mask Dataset/&quot;
dataset = {&#39;image_path&#39; : [], &#39;mask_status&#39; : [], &#39;where&#39;: []}

for where in os.listdir(path): # where은 위에서 보았다시피 Test, Train, Validation이 된다
    for status in os.listdir(path + &quot;/&quot; + where): # WithMask , WithoutMask
        for image in glob.glob(path + where + &#39;/&#39; + status + &#39;/&#39; + &#39;*.png&#39;):
            dataset[&#39;image_path&#39;].append(image)
            dataset[&#39;mask_status&#39;].append(status)
            dataset[&#39;where&#39;].append(where)

# 데이터프레임화
dataset = pd.DataFrame(dataset)
dataset.head()</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/6834ca04-5423-4ba0-943b-31a9deca0689/image.png" alt=""></p>
<br>

<p><strong>📌 데이터 확인</strong></p>
<pre><code class="language-python"># 랜덤하게 어떤 사진들이 있는지 확인
import cv2

plt.figure(figsize=(15, 10))
for i in range(9):
    random = np.random.randint(1, len(dataset))
    plt.subplot(3, 3, i + 1)
    plt.imshow(cv2.imread(dataset.loc[random, &#39;image_path&#39;]))
    plt.title(dataset.loc[random, &#39;mask_status&#39;], size = 15)
    plt.xticks([])
    plt.yticks([])
plt.show()</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/aee139c3-08f8-4662-ac9b-9231c1f09eb6/image.png" alt=""></p>
<p>grayscale을 적용시키지 않아 사진이 푸르게 나왔지만 어느정도 어떤 데이터가 들어있는지 확인은 가능하다.</p>
<p>우리는 마스크 착용 여부에 관한 모델링을 진행할 것이니, 각 Test, Train, Validation 폴더에 있던 사진들의 마스크 착용 여부 분포도 짚고 넘어가야한다.</p>
<p>만약 불균형인 데이터라면 oversampling이든 undersampling이든 샘플링을 통해 그 균형을 맞춰주어야한다. </p>
<pre><code># Train, Test, Val 데이터가 분리되어있는대로 사용
train_df = dataset[dataset[&#39;where&#39;] == &#39;Train&#39;]
test_df = dataset[dataset[&#39;where&#39;] == &#39;Test&#39;]
val_df = dataset[dataset[&#39;where&#39;] == &#39;Validation&#39;]

# Train, Test, Val 데이터 분포 확인
plt.figure(figsize=(15, 5))

plt.subplot(1, 3, 1)
sns.countplot(x = train_df[&#39;mask_status&#39;])
plt.title(&#39;Training Dataset&#39;, size = 10)

plt.subplot(1, 3, 2)
sns.countplot(x = test_df[&#39;mask_status&#39;])
plt.title(&#39;Test Dataset&#39;, size = 10)

plt.subplot(1, 3, 3)
sns.countplot(x = val_df[&#39;mask_status&#39;])
plt.title(&#39;Validation Dataset&#39;, size = 10)

plt.show()</code></pre><blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/956c8eeb-90e9-4d14-9826-174b8c15162c/image.png" alt=""></p>
<p>다행히 분포가 고르게 되어있어 따로 작업을 해줄 필요는 없어보인다.</p>
<br>

<h3 id="데이터-전처리">데이터 전처리</h3>
<p>이제 딥러닝을 돌리기 위한 데이터를 전처리해주어야한다.</p>
<pre><code class="language-python">data = []
image_size = 150

for i in range(len(train_df)):
    # grayscale 변환
    img_array = cv2.imread(train_df[&#39;image_path&#39;][i], cv2.IMREAD_GRAYSCALE)

    # resizing
    new_image_array = cv2.resize(img_array , (image_size, image_size))

    # encoding 
    # withmask를 1로 표시
    if train_df[&#39;mask_status&#39;][i] == &#39;WithMask&#39;:
        data.append([new_image_array, 1])
    else:
        data.append([new_image_array, 0])</code></pre>
<p>위 작업은 이미지로 되어있는 데이터를 grayscale로 변환하고 150 * 150 사이즈로 만들어준 후, 마스크 착용 여부(착용 -&gt; 1, 착용 X -&gt; 0)의 데이터를 만들어준 것이다.</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/b6219928-f504-4cec-8d54-3d29eab66796/image.png" alt=""></p>
<pre><code class="language-python"># 데이터 저장
X = []
y = []

for image in data:
    X.append(image[0])
    y.append(image[1])

X = np.array(X)
y = np.array(y)

# 그냥 한 번 더 데이터 분리
X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=13)</code></pre>
<p>그렇게 만들어진 데이터를 각각 X, y 변수에 두고 우리에겐 데이터가 폴더 별로 나누어져있긴 하지만 그냥 한 번 더 데이터를 분리 시켜 모델링을 해보았다.</p>
<br>

<h3 id="lenet">LeNet</h3>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/add2c86e-714e-44bc-a896-94bcf01f7060/image.png" alt=""></p>
<p>오늘 사용해볼 모델은 CNN의 개념이 도입된 LeNet이라는 모델이다. </p>
<p>CNN의 개념이 도입되었다란, convolution layer와 pooling, dropout 등 앞서 배웠던 이론으로 레이어를 쌓은 것을 말한다.</p>
<p>LeNet에도 여러 종류가 있고 현재 다양하게 변형이 되어서 어떤 것이 특정 LeNet이다!! 라고 말하기 어렵지만 그냥 가볍게 흐름만 파악하는 용으로 실습해보자.</p>
<pre><code class="language-python"># 모델링(LeNet)
from tensorflow.keras import layers, models

model = models.Sequential([
    layers.Conv2D(32, kernel_size = (5, 5), strides = (1, 1), padding = &#39;same&#39;,
                  activation = &#39;relu&#39;, input_shape = (150, 150, 1)),
    layers.MaxPooling2D(pool_size = (2, 2), strides = (2, 2)),
    layers.Conv2D(64, (2, 2), activation = &#39;relu&#39;, padding = &#39;same&#39;),
    layers.MaxPooling2D(pool_size = (2, 2)),
    layers.Dropout(0.25),
    layers.Flatten(),
    layers.Dense(1000, activation = &#39;relu&#39;),
    layers.Dense(1, activation = &#39;sigmoid&#39;) # 단일분류이므로 출력은 1개로
])

model.compile(optimizer = &#39;adam&#39;, loss = tf.keras.losses.BinaryCrossentropy(), 
              metrics = [&#39;accuracy&#39;])


# 학습
# (크기, 해상도1, 해상도2, 채널)
X_train = X_train.reshape(len(X_train), X_train.shape[1], X_train.shape[2], 1)
X_val = X_val.reshape(len(X_val), X_val.shape[1], X_val.shape[2], 1)
history = model.fit(X_train, y_train, epochs=4, batch_size=32)

# 성능
model.evaluate(X_val, y_val)</code></pre>
<p>validation set에서의 성능은 loss: 0.0970 - accuracy: 0.9710 로, 꽤 좋은 성능을 보이는 것 같다.</p>
<pre><code class="language-python"># 0.5보다 큰 경우 1로 취급
prediction = (model.predict(X_val) &gt; 0.5).astype(&#39;int32&#39;)

print(classification_report(y_val, prediction))
print(confusion_matrix(y_val, prediction))</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/cf9a861e-6be0-4403-890f-887f231ee6c4/image.png" alt=""></p>
<p>그리고 0.5보다 큰 것은 마스크를 착용했다고 하고 classification_report와 confusion_matrix를 그려보아도 꽤 괜찮아보인다.</p>
<p>제로베이스를 수강하며 틀리게 예측한 것에 대해 왜 틀리게 예측한 것인지 들여다 볼 필요성을 은근 강조하고 계시는 걸 느꼈다. </p>
<p>그래서 이번에도 틀린 것에 대해 확인해본 결과 아래와 같았다.</p>
<pre><code class="language-python"># 틀린 것에 대한 확인
wrong_result = []
for n in range(0,len(y_val)):
    if prediction[n] != y_val[n]:
        wrong_result.append(n)
len(wrong_result) # 58

import random

samples = random.choices(population = wrong_result, k = 6)

plt.figure(figsize = (14, 12))

for idx, n in enumerate(samples):
    plt.subplot(2, 3, idx + 1)
    plt.imshow(X_val[n].reshape(150, 150), interpolation=&#39;nearest&#39;)
    plt.title(prediction[n])
    plt.axis(&#39;off&#39;)

plt.show()</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/48a8ceba-c55c-4f16-83e2-05a187a7ff80/image.png" alt=""></p>
<p>사진이 확대되어있거나, 흐린 부분과 같은 사진들은 잘 인식을 못 하는 것 같다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[딥러닝] 순방향 연산 및 역전파 / 크로스엔트로피 ]]></title>
            <link>https://velog.io/@castle_mi/%EB%94%A5%EB%9F%AC%EB%8B%9D-%EC%88%9C%EB%B0%A9%ED%96%A5-%EC%97%B0%EC%82%B0-%EB%B0%8F-%EC%97%AD%EC%A0%84%ED%8C%8C-%ED%81%AC%EB%A1%9C%EC%8A%A4%EC%97%94%ED%8A%B8%EB%A1%9C%ED%94%BC</link>
            <guid>https://velog.io/@castle_mi/%EB%94%A5%EB%9F%AC%EB%8B%9D-%EC%88%9C%EB%B0%A9%ED%96%A5-%EC%97%B0%EC%82%B0-%EB%B0%8F-%EC%97%AD%EC%A0%84%ED%8C%8C-%ED%81%AC%EB%A1%9C%EC%8A%A4%EC%97%94%ED%8A%B8%EB%A1%9C%ED%94%BC</guid>
            <pubDate>Tue, 31 Oct 2023 13:26:48 GMT</pubDate>
            <description><![CDATA[<p>✍🏻 31일 공부 이야기.</p>
<p><a href="https://github.com/castlemi99/ZeroBaseDataSchool/blob/main/Deep%20Learning/2.%20Deep%20Learning%20from%20scratch.ipynb"><img src="https://velog.velcdn.com/images/castle_mi/post/ed710927-7af0-4d26-aa2f-4bc0b2ad1e91/image.png" alt=""></a>
오늘 공부한 실습 코드는 위 깃허브 사진을 클릭하면 이동합니다 :) </p>
<p>본격적으로 오늘 배울 내용을 정리하기 전, 간단하게 딥러닝에 대한 인사이트(?) 몇 개만 보고 가시죵</p>
<p>📌 지난 블로그에서 정리했던 활성화 함수의 종류 총 정리
<img src="https://velog.velcdn.com/images/castle_mi/post/a9244549-3d52-4535-b825-fc089ba6e805/image.png" alt=""></p>
<p>📌 규모에 따른 딥러닝 모델 종류 정리
<img src="https://velog.velcdn.com/images/castle_mi/post/341aa19b-25be-46ae-a523-8c03688cd7ae/image.png" alt=""></p>
<br>

<h1 id="deep-learning-from-scratch">Deep Learning from scratch</h1>
<h2 id="순방향-연산">순방향 연산</h2>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/179bdee3-92cc-40b1-9bd5-570b6bd14764/image.png" alt="">
왼쪽에서 오른쪽의 방향으로 가는, 입력에서 가중치와 편향을 주고 활성 함수를 통과시켜 출력값을 얻는 과정을 &lt;순방향 연산&gt;이라고 한다.</p>
<p>흐름만 살짝 살펴보자면 아래와 같다.</p>
<p>📌 데이터 준비</p>
<pre><code class="language-python">import numpy as np

X = np.array([
    [0,0,1], 
    [0,1,1],
    [1,0,1],
    [1,1,1]
])</code></pre>
<p>📌 활성화 함수</p>
<pre><code class="language-python"># 활성화 함수 sigmoid
def sigmoid(x):
    return 1.0 / (1.0 + np.exp(-x))</code></pre>
<p><strong>📌 순방향 연산(출력값 얻어내기)</strong></p>
<pre><code class="language-python">W = 2 * np.random.random((1, 3)) -1 # 랜덤하게 선택된 가중치


# 추론(순방향 연산)
N = 4

for k in range(N):
    x = X[k, :].T 
    v = np.matmul(W, x)
    y = sigmoid(v)

    print(y)</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>[0.30389718]
[0.33752218]
[0.53475609]
[0.57290196]</p>
<p>위 과정처럼 데이터에 가중치와 편향을 주고 활성화함수를 거쳐 출력값을 얻는 흐름을 순방향 연산이라고 한다. </p>
<p>지금은 가중치를 랜덤하게 선택하게 하고 음수에서부터 양수까지 다양한 값을 가지게 하기 위해 2를 곱하고 1을 빼주는 작업을 실시해주었는데, 
실제로는 학습된 가중치를 주어야한다.</p>
<p><em>그렇다면 학습된 가중치는 어떻게 찾아낼 수 있을까?</em></p>
<h2 id="학습-원리">학습 원리</h2>
<p>먼저 학습을 위해선 정답지가 필요하다. </p>
<p>정답지를 주고 학습된 가중치를 얻어보자.</p>
<pre><code class="language-python"># 정답지
D = np.array([
    [0], [0], [1], [1]
])</code></pre>
<p>우리는 이제 정답지도 있으니, 출력값과 정답지와의 오차를 구해 그 오차가 작아지도록 하는 가중치를 구하면 된다.</p>
<pre><code class="language-python"># 출력값 계산
def calc_output(W, x):
    v = np.matmul(W, x)
    y = sigmoid(v)
    return y

# 오차 계산
def calc_error(d, y_pred):
    e = d - y_pred
    delta = y_pred * (1 - y_pred) * e
    return delta</code></pre>
<p>반복되는 출력값과 오차의 계산을 함수로 표현해 간단하게 만들어 주었다. </p>
<p>그런데 <code>calc_error</code>함수에 있는 <code>e</code>는 출력값과 실제값의 차이이지만 <code>delta</code>는 어떻게 나온 식일까?</p>
<p>이를 알기 위해선 시그모이드 함수를 다시 살펴볼 필요가 있다.</p>
<pre><code class="language-python"># 시그모이드 함수의 미분 형태
import sympy as sym

z = sym.Symbol(&#39;z&#39;)
s = 1 / (1 + sym.exp(-z)) # 시그모이드 함수 형태
sym.diff(s) # 시그모이드 함수 미분 형태</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>$(1 + e^{−z}) \over(1 + e^{−z})^2$  </p>
<p>파이썬의 <code>sympy</code>를 이용하면 위와 같이 미분식을 쉽게 얻어낼 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/959cf890-986d-4134-b495-55d2b1f12383/image.png" alt=""></p>
<p>위 사진의 화살표 부분의 식이 도출이 된 것이고 
이 식을 좀 더 전개해나아가다 보면 하이라이트 된 식과 같이 시그모이드 값으로 변환시킬 수 있다.</p>
<p>그래서 <code>delta</code>값의 <code>y_pred * (1 - y_pred) * e</code> 의 <code>y_pred * (1 - y_pred)</code>부분은 <code>e</code>의 미분값임을 알 수 있고
오차를 뒤로 전해줄 때 단순히 차이만 계산하는 것이 아닌 미분값을 같이 곱해서 넘겨주는 것이다.</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/e7932b8d-db38-4c7b-ae9f-dc6d9c6a3b5d/image.png" alt=""></p>
<p>미분값을 같이 곱해주는 이유는 위와 같은데, 아마 &lt;역전파&gt;파트에서 더 자세히 다룰 예정이다.</p>
<p><strong>📌 Gradient Descent를 이용한 W 업데이트 식</strong></p>
<pre><code class="language-python"># 한 epoch에 수행되는 W 계산(gradient descent)
def delta_GD(W, X, D, alpha):
    # 모든 데이터에 대해 계산하는 gradient descent 방식에 따라
    # 전체 데이터 개수인 4개를 다 거쳐야함
    for k in range(4):
        x = X[k, :].T # 입력값
        d = D[k] # 실제값(정답지)

        y_pred = calc_output(W, x) # 순방향 추론
        delta = calc_error(d , y_pred) # delta 계산

        dW = alpha * delta * x # 가중치 변화량
        W = W + dW # 가중치 업데이트
    return W</code></pre>
<pre><code class="language-python">alpha = 0.9
for epoch in range(1000):
    W = delta_GD(W, X, D, alpha)
    print(W)</code></pre>
<p>이렇게 계산된 함수를 이용해 기존 랜덤하게 선택된 가중치를 학습을 통해 업데이트 시켜나가면 <code>array([[ 7.15327831, -0.22405033, -3.35616859]])</code>라는 최종 값을 얻게 되고</p>
<pre><code class="language-python">calc_output(W, X.T)</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>array([[0.03369375, 0.02711394, 0.97805678, 0.97269656]])</p>
<p>위 식을 통해 학습된 가중치로 얻어낸 예측값도 확인할 수 있다.</p>
<p>초반 랜덤으로 선택된 가중치로 얻어낸 </p>
<blockquote>
<p>[0.30389718]
[0.33752218]
[0.53475609]
[0.57290196]</p>
</blockquote>
<p>값과는 달리 정답지(0,0,1,1)에 근사한 값이 나온 것을 볼 수 있다.</p>
<br>


<h2 id="역전파">역전파</h2>
<p>역전파에 대해... 대학 시절 수학적인 이론부터 배운 사람으로서(??) 다시 그 기억을 끄집어내어 정리하고, 블로그에도 올리고 싶었지만 강의 양이 너무 많아서 할 수가 없었다 😭 </p>
<blockquote class="instagram-media" data-instgrm-captioned data-instgrm-permalink="https://www.instagram.com/p/CtOvdDqP2dp/?utm_source=ig_embed&amp;utm_campaign=loading" data-instgrm-version="14" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:540px; min-width:326px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);"><div style="padding:16px;"> <a href="https://www.instagram.com/p/CtOvdDqP2dp/?utm_source=ig_embed&amp;utm_campaign=loading" style=" background:#FFFFFF; line-height:0; padding:0 0; text-align:center; text-decoration:none; width:100%;" target="_blank"> <div style=" display: flex; flex-direction: row; align-items: center;"> <div style="background-color: #F4F4F4; border-radius: 50%; flex-grow: 0; height: 40px; margin-right: 14px; width: 40px;"></div> <div style="display: flex; flex-direction: column; flex-grow: 1; justify-content: center;"> <div style=" background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; margin-bottom: 6px; width: 100px;"></div> <div style=" background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; width: 60px;"></div></div></div><div style="padding: 19% 0;"></div> <div style="display:block; height:50px; margin:0 auto 12px; width:50px;"><svg width="50px" height="50px" viewBox="0 0 60 60" version="1.1" xmlns="https://www.w3.org/2000/svg" xmlns:xlink="https://www.w3.org/1999/xlink"><g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"><g transform="translate(-511.000000, -20.000000)" fill="#000000"><g><path d="M556.869,30.41 C554.814,30.41 553.148,32.076 553.148,34.131 C553.148,36.186 554.814,37.852 556.869,37.852 C558.924,37.852 560.59,36.186 560.59,34.131 C560.59,32.076 558.924,30.41 556.869,30.41 M541,60.657 C535.114,60.657 530.342,55.887 530.342,50 C530.342,44.114 535.114,39.342 541,39.342 C546.887,39.342 551.658,44.114 551.658,50 C551.658,55.887 546.887,60.657 541,60.657 M541,33.886 C532.1,33.886 524.886,41.1 524.886,50 C524.886,58.899 532.1,66.113 541,66.113 C549.9,66.113 557.115,58.899 557.115,50 C557.115,41.1 549.9,33.886 541,33.886 M565.378,62.101 C565.244,65.022 564.756,66.606 564.346,67.663 C563.803,69.06 563.154,70.057 562.106,71.106 C561.058,72.155 560.06,72.803 558.662,73.347 C557.607,73.757 556.021,74.244 553.102,74.378 C549.944,74.521 548.997,74.552 541,74.552 C533.003,74.552 532.056,74.521 528.898,74.378 C525.979,74.244 524.393,73.757 523.338,73.347 C521.94,72.803 520.942,72.155 519.894,71.106 C518.846,70.057 518.197,69.06 517.654,67.663 C517.244,66.606 516.755,65.022 516.623,62.101 C516.479,58.943 516.448,57.996 516.448,50 C516.448,42.003 516.479,41.056 516.623,37.899 C516.755,34.978 517.244,33.391 517.654,32.338 C518.197,30.938 518.846,29.942 519.894,28.894 C520.942,27.846 521.94,27.196 523.338,26.654 C524.393,26.244 525.979,25.756 528.898,25.623 C532.057,25.479 533.004,25.448 541,25.448 C548.997,25.448 549.943,25.479 553.102,25.623 C556.021,25.756 557.607,26.244 558.662,26.654 C560.06,27.196 561.058,27.846 562.106,28.894 C563.154,29.942 563.803,30.938 564.346,32.338 C564.756,33.391 565.244,34.978 565.378,37.899 C565.522,41.056 565.552,42.003 565.552,50 C565.552,57.996 565.522,58.943 565.378,62.101 M570.82,37.631 C570.674,34.438 570.167,32.258 569.425,30.349 C568.659,28.377 567.633,26.702 565.965,25.035 C564.297,23.368 562.623,22.342 560.652,21.575 C558.743,20.834 556.562,20.326 553.369,20.18 C550.169,20.033 549.148,20 541,20 C532.853,20 531.831,20.033 528.631,20.18 C525.438,20.326 523.257,20.834 521.349,21.575 C519.376,22.342 517.703,23.368 516.035,25.035 C514.368,26.702 513.342,28.377 512.574,30.349 C511.834,32.258 511.326,34.438 511.181,37.631 C511.035,40.831 511,41.851 511,50 C511,58.147 511.035,59.17 511.181,62.369 C511.326,65.562 511.834,67.743 512.574,69.651 C513.342,71.625 514.368,73.296 516.035,74.965 C517.703,76.634 519.376,77.658 521.349,78.425 C523.257,79.167 525.438,79.673 528.631,79.82 C531.831,79.965 532.853,80.001 541,80.001 C549.148,80.001 550.169,79.965 553.369,79.82 C556.562,79.673 558.743,79.167 560.652,78.425 C562.623,77.658 564.297,76.634 565.965,74.965 C567.633,73.296 568.659,71.625 569.425,69.651 C570.167,67.743 570.674,65.562 570.82,62.369 C570.966,59.17 571,58.147 571,50 C571,41.851 570.966,40.831 570.82,37.631"></path></g></g></g></svg></div><div style="padding-top: 8px;"> <div style=" color:#3897f0; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:550; line-height:18px;">Instagram에서 이 게시물 보기</div></div><div style="padding: 12.5% 0;"></div> <div style="display: flex; flex-direction: row; margin-bottom: 14px; align-items: center;"><div> <div style="background-color: #F4F4F4; border-radius: 50%; height: 12.5px; width: 12.5px; transform: translateX(0px) translateY(7px);"></div> <div style="background-color: #F4F4F4; height: 12.5px; transform: rotate(-45deg) translateX(3px) translateY(1px); width: 12.5px; flex-grow: 0; margin-right: 14px; margin-left: 2px;"></div> <div style="background-color: #F4F4F4; border-radius: 50%; height: 12.5px; width: 12.5px; transform: translateX(9px) translateY(-18px);"></div></div><div style="margin-left: 8px;"> <div style=" background-color: #F4F4F4; border-radius: 50%; flex-grow: 0; height: 20px; width: 20px;"></div> <div style=" width: 0; height: 0; border-top: 2px solid transparent; border-left: 6px solid #f4f4f4; border-bottom: 2px solid transparent; transform: translateX(16px) translateY(-4px) rotate(30deg)"></div></div><div style="margin-left: auto;"> <div style=" width: 0px; border-top: 8px solid #F4F4F4; border-right: 8px solid transparent; transform: translateY(16px);"></div> <div style=" background-color: #F4F4F4; flex-grow: 0; height: 12px; width: 16px; transform: translateY(-4px);"></div> <div style=" width: 0; height: 0; border-top: 8px solid #F4F4F4; border-left: 8px solid transparent; transform: translateY(-4px) translateX(8px);"></div></div></div> <div style="display: flex; flex-direction: column; flex-grow: 1; justify-content: center; margin-bottom: 24px;"> <div style=" background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; margin-bottom: 6px; width: 224px;"></div> <div style=" background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; width: 144px;"></div></div></a><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;"><a href="https://www.instagram.com/p/CtOvdDqP2dp/?utm_source=ig_embed&amp;utm_campaign=loading" style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;" target="_blank">데이터분석가 황성미(@castle._.mi_data)님의 공유 게시물</a></p></div></blockquote> <script async src="//www.instagram.com/embed.js"></script>

<p>지금은 공스타그램에 업로드해둔 링크를 첨부해두고 역전파에 대해서는 꼭 다시 정리해둬야지...!!</p>
<p>이전 블로그에서 실습했던 XOR문제를 다시 실습해보자.</p>
<pre><code class="language-python"># 데이터 준비
import numpy as np

X = np.array([[0,0,1],
              [0,1,1],
              [1,0,1],
              [1,1,1]])
D = np.array([[0], [1], [1], [0]])

W = 2 * np.random.random((1, 3)) -1


# 학습을 통한 가중치 업데이트
alpha = 0.9
for epoch in range(1000):
    W = delta_GD(W, X, D, alpha)


# 학습된 결과를 통해 예측된 값
calc_output(W, X.T) </code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>array([[0.52965337, 0.5       , 0.47034663, 0.44090112]])</p>
<p>위에서 사용했던 함수들을 사용해서 예측해보았는데 생각보다 잘 된 것같진 않다. </p>
<p>레이어를 한 단계 더 쌓고 역전파를 이용해 다시 예측해보자.</p>
<p>레이어를 쌓을 때는 항상 계산되는 <code>shape</code>을 신경써서 코드를 짜주어야한다.</p>
<p>나는 강의 코드를 따라 쳤음에도 자꾸 에러가 떠서.. 일일이 다 shape도 확인해보고 어디를 수정해줘야하는지 찾아보고 그랬다... 🫥🤯 이거시 딥러닝이지....
(딥러닝의 세계에 온 것을 환영한다는걸 알리는 듯... ㅎㅎ)</p>
<pre><code class="language-python">def calc_output(W1, W2, x):
    # W1 (4, 3) 
    # x = X[k, :].T (3, )
    v1 = np.matmul(W1, x)
    y1 = sigmoid(v1) # (4, )
    # W2 (1, 4)
    v = np.matmul(W2, y1)
    y = sigmoid(v) # (1, )

    # print(&#39;y : &#39;, y.shape) # (1, )
    # print(&#39;y1 : &#39;, y1.shape) # (4, )
    return y, y1

# 출력층 델타 계산
def calc_delta(d, y):
    e = d - y
    delta = y*(1-y)*e
    # print(&#39;delta : &#39;, delta.shape) # (1, )
    return delta

# 은닉층 델타 계산
def calc_delta1(W2, delta, y1):
    e1 = np.matmul(W2.T, delta)
    delta1 = y1*(1-y1)*e1
    # print(&#39;delta1 : &#39;, delta1.shape) # (4,)
    return delta1

# 역전파를 이용한 가중치 업데이트 계산 함수
def backprop_XOR(W1, W2, X, D, alpha):
    for k in range(4):
        x = X[k, :].T #(3, )
        d = D[k]

        y, y1 = calc_output(W1, W2, x)
        delta = calc_delta(d, y)
        delta1 = calc_delta1(W2, delta, y1)

        # alpha : 상수 /  delta1 : (4 , ) / x : (3 , )
        dW1 = (alpha * delta1).reshape(4, 1) * x.reshape(1, 3)
        W1 = W1 + dW1

        # alpha : 상수 /  delta : (1 , ) / y1 : (4, )
        dW2 = alpha * delta * y1
        W2 = W2 + dW2

    return W1, W2</code></pre>
<p>레이어를 하나 더 쌓아준 다음 출력층과 은닉층의 델타를 계산하고 계산된 델타를 통해 가중치를 업데이트하는 함수를 작성했다.</p>
<p>이들을 이용해 다시 가중치를 랜덤하게 초기화하고 다시 학습시켜보면 아래와 같다.</p>
<pre><code class="language-python">X = np.array([[0,0,1],
              [0,1,1],
              [1,0,1],
              [1,1,1]])
D = np.array([[0], [1], [1], [0]])

W1 = 2 * np.random.random((4, 3)) -1 # 첫번째 가중치
W2 = 2 * np.random.random((1, 4)) -1 # 두번째 가중치


# 학습
alpha = 0.9
for epoch in range(10000):
    W1, W2 = backprop_XOR(W1, W2, X, D, alpha)

# 예측 결과    
for k in range(4):
    x = X[k, :].T
    y, y1 = calc_output(W1, W2, x)
    print(y)    
</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>[0.006909]
[0.99022793]
[0.9896984]
[0.01419835]</p>
<p>실제값인 0,1,1,0과 유사한 값을 보이는 것을 확인할 수 있다. </p>
<br>


<h2 id="크로스엔트로피">크로스엔트로피</h2>
<p>그동안 loss함수를 mse로 많이 사용해왔다.</p>
<p>이번에는 다른 종류의 loss 함수 중 크로스 엔트로피에 대해 알아보고 이를 이용한 가중치 업데이트도 실습해보자.</p>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/253d3115-ec01-493f-b027-da42e4174b96/image.png" alt=""></th>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/d0c8cb8b-4a9b-49ea-ba7f-bf367321e7b2/image.png" alt=""></th>
</tr>
</thead>
</table>
<p>크로스엔트로피는 분류 문제에서 흔히 사용되며 U자의 형태를 띄고 있다.</p>
<p>함수를 만들기 위해서는 앞에서 했던 것처럼 미분식을 알아야한다.</p>
<p><a href="https://velog.io/@hjk1996/Cross-Entropy%EC%99%80-Softmax%EC%9D%98-%EB%AF%B8%EB%B6%84#:~:text=3.%20Cross%2DEntropy%EC%9D%98%20%EB%AF%B8%EB%B6%84"><img src="https://velog.velcdn.com/images/castle_mi/post/dc1de41b-46dd-4c99-9132-ac4f23c0529d/image.png" alt=""></a></p>
<p>크로스 엔트로피 함수의 미분식에 대해 정리된 글을 찾다가 위 글을 발견했다. </p>
<p>위의 수식을 따라가다보면 우리는 크로스 엔트로피 함수의 미분식은 결국 <code>예측값 - 실제값</code>이라는 사실을 알 수 있다.</p>
<p>그러므로 아래와 같이 함수를 만들 수 있고 그 결과까지 한 번 살펴봐보자.</p>
<p><strong>📌 cross-entropy를 이용한 함수</strong></p>
<pre><code class="language-python"># cross entropy의 출력층 델타
def calc_crossentropy_delta(d, y):
    # d : 실제값, y : 예측값
    e = d - y
    delta = e
    return delta

# cross entropy의 은닉층 델타 
def calc_crossentropy_delta1(W2, delta, y1):
    e1 = np.matmul(W2.T, delta)
    delta1 = y1*(1-y1)*e1
    return delta1

# 역전파를 이용한 가중치 업데이트 계산 함수
def backprop_CE(W1, W2, X, D, alpha):
    for k in range(4):
        x = X[k, :].T 
        d = D[k]

        y, y1 = calc_output(W1, W2, x)
        delta = calc_crossentropy_delta(d, y)
        delta1 = calc_crossentropy_delta1(W2, delta, y1)

        dW1 = (alpha * delta1).reshape(4, 1) * x.reshape(1, 3)
        W1 = W1 + dW1

        dW2 = alpha * delta * y1
        W2 = W2 + dW2

    return W1, W2</code></pre>
<br>

<p>📌 결과 확인</p>
<pre><code class="language-python">W1 = 2 * np.random.random((4, 3)) -1 # 첫번째 가중치
W2 = 2 * np.random.random((1, 4)) -1 # 두번째 가중치

# 학습
alpha = 0.9
for epoch in range(10000):
    W1, W2 = backprop_CE(W1, W2, X, D, alpha)

# 결과
for k in range(4):
    x = X[k, :].T
    y, y1 = calc_output(W1, W2, x)
    print(y)</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>[8.49056272e-05]
[0.99990643]
[0.99990171]
[0.00019382]</p>
<p>이 또한 적절하게 예측값이 나온다 😊😊</p>
<p><br><br></p>
<h2 id="예제">예제</h2>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/0220632b-5212-458a-b3ee-7e0503a7a7e1/image.png" alt=""></p>
<p>이번에는 위와 같이 하나의 칸을 색깔로 채워 숫자를 만든 다음 해당 숫자를 잘 인식할 수 있는 모델을 만들어보자.</p>
<p>미리 결론부터 말하자면... 내가 했을 때엔 예측값이 거의 다 틀렸다 ㅎㅎ</p>
<p><code>alpha</code>와 <code>epoch</code> 값도 달리해가며 여러 번 돌려봤는데 제대로 학습이 안 되었나부다.. ㅠ </p>
<p>그래도 정리는 해야하니깐~!!!!ㅋㅋㅋㅋ</p>
<br>

<p>📌 데이터 준비</p>
<pre><code class="language-python">X = np.zeros((5, 5, 5))

X[:, :, 0] = [[0,1,1,0,0], [0,0,1,0,0], [0,0,1,0,0], [0,0,1,0,0], [0,1,1,1,0]]
X[:, :, 1] = [[1,1,1,1,0], [0,0,0,0,1], [0,1,1,1,0], [1,0,0,0,0], [1,1,1,1,1]]
X[:, :, 2] = [[1,1,1,1,0], [0,0,0,0,1], [0,1,1,1,0], [0,0,0,0,1], [1,1,1,1,0]]
X[:, :, 3] = [[0,0,0,1,0], [0,0,1,1,0], [0,1,0,1,0], [1,1,1,1,1], [0,0,0,1,0]]
X[:, :, 4] = [[1,1,1,1,1], [1,0,0,0,0], [1,1,1,1,0], [0,0,0,0,1], [1,1,1,1,0]]

# 정답지
D = np.array([[[1,0,0,0,0]], [[0,1,0,0,0]], [[0,0,1,0,0]], [[0,0,0,1,0]], [[0,0,0,0,1]]])

plt.figure(figsize=(12, 4))
for n in range(5):
    plt.subplot(1, 5, n+1)
    plt.imshow(X[:, :, n])
plt.show()</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/49dde583-3d99-413f-9946-bc186baf8c8b/image.png" alt=""></p>
<p>위와 같이 코드를 짜면 아까 초반에 보았던 그림으로 그린 1, 2, 3, 4, 5가 만들어진다.</p>
<p>우리는 이 숫자들을 <strong>RELU 3번, Softmax 1번을 거친 모델</strong>로 학습시켜 볼 것이다.</p>
<br>

<p><strong>📌 ReLU와 Softmax 함수 계산 및 순방향 계산 함수</strong></p>
<pre><code class="language-python"># softmax
def softmax(x):
    # subtract : 두 값의 차이 계산
    # 최대값과의 차이를 exp 해줌으로써 일종의 min-max scaler를 적용해 overflow를 막아준 셈
    x = np.subtract(x, np.max(x)) 
    ex = np.exp(x)

    return ex / np.sum(ex)

# relu : 음수는 모두 0으로 이외의 값은 해당 값으로 반환해줌
def ReLU(x):
    # np.maximum : 여러 array 사이에서 각 위치의 최대값 반환
    return np.maximum(0, x)


# 순방향 계산
def calc_output_relu(W1, W2, W3, W4, x):
    v1 = np.matmul(W1, x)
    y1 = ReLU(v1)
    v2 = np.matmul(W2, y1)
    y2 = ReLU(v2)
    v3 = np.matmul(W3, y2)
    y3 = ReLU(v3)
    v = np.matmul(W4, y3)
    y = softmax(v)

    return y, v1, v2, v3, y1, y2, y3</code></pre>
<br>

<p><strong>📌 델타 계산 및 가중치 업데이트 계산 함수</strong></p>
<pre><code class="language-python"># 역전파를 이용한 델타 계산
def backprop_ReLU(d, y, W2, W3, W4, v1, v2, v3):
    e = d - y
    delta = e

    e3 = np.matmul(W4.T, delta)
    delta3 = (v3 &gt; 0)*e3

    e2 = np.matmul(W3.T, delta3)
    delta2 = (v2 &gt; 0)*e2

    e1 = np.matmul(W2.T, delta2)
    delta1 = (v1 &gt; 0)*e1

    return delta, delta1, delta2, delta3


# 델타를 이용한 가중치 업데이트 계산
def calc_Ws(alpha, delta, delta1, delta2, delta3, y1, y2, y3, x, W1, W2, W3, W4):
    dW4 = alpha*delta*y3.T
    W4 = W4 + dW4

    dW3 = alpha*delta3*y2.T
    W3 = W3 + dW3

    dW2 = alpha*delta2*y1.T
    W2 = W2 + dW2

    dW1 = alpha*delta1*x.T
    W1 = W1 + dW1

    return W1, W2, W3, W4


# 가중치 업데이트
def DeepReLU(W1, W2, W3, W4, X, D, alpha):
    for k in range(5):
        x = np.reshape(X[:, :, k], (25, 1))
        d =D[k, :].T

        y, v1, v2, v3, y1, y2, y3 = calc_output_relu(W1, W2, W3, W4, x)
        delta, delta1, delta2, delta3 = backprop_ReLU(d, y, W2, W3, W4, v1, v2, v3)
        W1, W2, W3, W4 = calc_Ws(alpha, delta, delta1, delta2, delta3, y1, y2, y3, x, W1, W2, W3, W4)

        return W1, W2, W3, W4</code></pre>
<br>


<p>📌 학습 및 검정 결과</p>
<pre><code class="language-python"># 학습
W1 = 2*np.random.random((20, 25)) -1
W2 = 2*np.random.random((20, 20)) - 1
W3 = 2*np.random.random((20, 20)) - 1
W4 = 2*np.random.random((5, 20)) - 1

alpha = 0.001
for epoch in tqdm_notebook(range(10000)):
    W1, W2, W3, W4 = DeepReLU(W1, W2, W3, W4, X, D, alpha)


# 검증
def verify_algorithm(x, W1, W2, W3, W4):
    v1 = np.matmul(W1, x)
    y1 = ReLU(v1)

    v2 = np.matmul(W2, y1)
    y2 = ReLU(v2)

    v3 = np.matmul(W3, y2)
    y3 = ReLU(v3)

    v = np.matmul(W4, y3)
    y = softmax(v)

    return y

# 결과
N= 5
for k in range(N):
    x = np.reshape(X[:, :, k], (25, 1))
    y = verify_algorithm(x, W1, W2, W3, W4)
    print(&quot;Y = {} : &quot;.format(k+1)) # 실제값
    print(np.argmax(y, axis=0)+1)  # 예측값 
    print(y)
    print(&#39;--------------------&#39;)</code></pre>
<br>

<p>📌 테스트 데이터에 대한 예측 및 결과</p>
<pre><code class="language-python"># 테스트
X_test = np.zeros((5, 5, 5))

X_test[:, :, 0] = [[0,0,0,0,0], [0,1,0,0,0], [1,0,1,0,0], [0,0,1,0,0], [0,1,1,1,0]]
X_test[:, :, 1] = [[1,1,1,1,0], [0,0,0,0,0], [0,1,1,1,0], [0,0,0,0,1], [1,1,1,1,0]]
X_test[:, :, 2] = [[0,0,0,1,0], [0,0,1,1,0], [0,1,0,0,0], [1,1,1,0,1], [0,0,0,1,0]]
X_test[:, :, 3] = [[1,1,1,1,0], [0,0,0,0,1], [0,1,1,1,0], [1,0,0,0,0], [1,1,1,1,0]]
X_test[:, :, 4] = [[0,1,1,1,1], [1,1,0,0,0], [1,1,1,1,0], [0,0,0,1,1], [1,1,1,1,0]]


# 예측
learning_result = [0,0,0,0,0]

for k in range(N):
    x = np.reshape(X_test[:, :, k], (25, 1))
    y = verify_algorithm(x, W1, W2, W3, W4)

    learning_result[k] = np.argmax(y, axis = 0) + 1

plt.figure(figsize=(12, 4))
for k in range(5):
    plt.subplot(2, 5, k+1)
    plt.imshow(X_test[:, :, k]) # 원본 데이터

    plt.subplot(2, 5, k+6)
    plt.imshow(X_test[:, :, learning_result[k][0] - 1]) # 예측 데이터
plt.show()</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/1b808de6-07eb-4c3b-91dd-22cd33f0e94c/image.png" alt=""></p>
<p>모델이 멍텅구리인가... 1만 맞추고 다 틀렸다 ㅎㅎ</p>
<p>그래서 모델에 융통성을 길러주기 위해 <code>Dropout</code>도 추가해보았다.</p>
<h3 id="dropout">DROPOUT</h3>
<p><strong>📌 DROPOUT 함수 및 순방향 계산</strong></p>
<pre><code class="language-python"># dropout 함수
def Dropout(y, ratio):
    ym = np.zeros_like(y) 

    num = round(y.size * (1 - ratio)) 
    # y.size까지의 수 중 num 개수만큼 랜덤으로 추출
    idx = np.random.choice(y.size, num, replace = True)
    ym[idx] = 1.0 / (1.0 - ratio)

    return ym

# dropout 적용시킨 순방향 출력 계산
def calc_output_dropout(W1, W2, W3, W4, x):  
    v1 = np.matmul(W1, x)
    y1 = sigmoid(v1)
    y1 = y1 * Dropout(y1, 0.2)

    v2 = np.matmul(W2, y1)
    y2 = sigmoid(v2)
    y1 = y2 * Dropout(y2, 0.2)

    v3 = np.matmul(W3, y2)
    y3 = sigmoid(v3)
    y3 = y3 * Dropout(y3, 0.2)

    v = np.matmul(W4, y3)
    y = softmax(v)

    return y, y1, y2, y3, v1, v2, v3</code></pre>
<br>

<p><strong>📌 델타 계산 및 가중치 업데이트</strong></p>
<pre><code class="language-python"># 역전파를 이용한 델타 계산
def backprop_dropout(d, y, y1, y2, y3, W2, W3, W4, v1, v2, v3):
    e = d - y
    delta = e

    e3 = np.matmul(W4.T, delta)
    delta3 = y3*(1 - y3)*e3

    e2 = np.matmul(W3.T, delta3)
    delta2 = y2*(1 - y2)*e2

    e1 = np.matmul(W2.T, delta2)
    delta1 = y1*(1 - y1)*e1

    return delta, delta1, delta2, delta3


# 가중치 업데이트
def Deepdropout(W1, W2, W3, W4, X, D):
    for k in range(5):
        x = np.reshape(X[:, :, k], (25, 1))
        d =D[k, :].T

        y, y1, y2, y3, v1, v2, v3 = calc_output_dropout(W1, W2, W3, W4, x)
        delta, delta1, delta2, delta3 = backprop_dropout(d, y, y1, y2, y3, W2, W3, W4, v1, v2, v3)
        W1, W2, W3, W4 = calc_Ws(alpha, delta, delta1, delta2, delta3, y1, y2, y3, x, W1, W2, W3, W4)

        return W1, W2, W3, W4</code></pre>
<br>

<p><strong>📌 학습 및 예측</strong></p>
<pre><code class="language-python"># 학습
W1 = 2*np.random.random((20, 25)) -1
W2 = 2*np.random.random((20, 20)) - 1
W3 = 2*np.random.random((20, 20)) - 1
W4 = 2*np.random.random((5, 20)) - 1

for epoch in tqdm_notebook(range(10000)):
    W1, W2, W3, W4 = Deepdropout(W1, W2, W3, W4, X, D)


# 테스트 데이터 다시 예측
learning_result = [0,0,0,0,0]

for k in range(N):
    x = np.reshape(X_test[:, :, k], (25, 1))
    y = verify_algorithm(x, W1, W2, W3, W4)

    learning_result[k] = np.argmax(y, axis = 0) + 1


plt.figure(figsize=(12, 4))
for k in range(5):
    plt.subplot(2, 5, k+1)
    plt.imshow(X_test[:, :, k]) # 원본 데이터

    plt.subplot(2, 5, k+6)
    plt.imshow(X_test[:, :, learning_result[k][0] - 1]) # 예측 데이터
plt.show()</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/facf8e1e-91d6-4506-94a2-b6b8b29d7cef/image.png" alt=""></p>
<p>어쩜... 더 틀릴 수가... 😭  </p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/b5bb2ddc-1a8e-4a6b-9019-a868ee8a7701/image.png" alt=""></p>
<p><code>alpha</code>와 <code>epoch</code>을 변화시켜도 성능이 향상되지 않는다면 위와 같은 방법을 고려해볼 수 있다고 한다.</p>
<p>대대적인 모델 공사가 필요한.. ㅎㅎ </p>
<p>일단 지금은 강의를 듣는 것이 급해서 더 이상 성능을 향상시켜보지는 못했다(아쉽..ㅠ)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[딥러닝] 회귀 , 단일 분류(XOR) , 다중 분류(iris, mnist) 실습으로 익히는 딥러닝 기본 / CNN]]></title>
            <link>https://velog.io/@castle_mi/%EB%94%A5%EB%9F%AC%EB%8B%9D</link>
            <guid>https://velog.io/@castle_mi/%EB%94%A5%EB%9F%AC%EB%8B%9D</guid>
            <pubDate>Tue, 31 Oct 2023 03:08:18 GMT</pubDate>
            <description><![CDATA[<p>✍🏻 30일 공부 이야기.</p>
<p><a href="https://github.com/castlemi99/ZeroBaseDataSchool/blob/main/Deep%20Learning/1.%20Beginning%20of%20Deep%20Learning.ipynb"><img src="https://velog.velcdn.com/images/castle_mi/post/b61a089c-6b84-4d63-977b-4f46ffa34fe9/image.png" alt=""></a>
오늘 공부한 내용은 위 깃허브에 올려두었습니다 :) 
사진을 클릭하면 깃허브로 이동해요 !</p>
<p><br><br></p>
<h1 id="딥러닝">딥러닝</h1>
<h2 id="tensorflow">TensorFlow</h2>
<p><code>!pip install tensorflow</code></p>
<p>머신러닝을 위한 오픈소스 플랫폼 및 딥러닝 프레임워크</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/c164db26-3c71-4317-980b-32395cdb74c1/image.png" alt="">
뉴런의 신경망에서 아이디어를 얻어서 딥러닝이 시작이 되었는데, </p>
<p>이때 신경망의 최소 단위를 뉴런이라 하고 
뉴런은 위 사진과 같이 입력, 가중치, 활성화 함수, 출력으로 구성되어있다.</p>
<p>뉴런에서 학습할 때 변하는 것은 가중치이며,
처음에는 초기화를 통해 랜덤값을 넣고 학습 과정에서 일정한 값으로 수렴하는 과정을 겪는다.</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/84cd3831-c45c-4a9a-adaa-b0cce210b65d/image.png" alt=""></p>
<p>뉴런들이 모여있는 형태를 레이러하고 하며 이들이 모여 망(net)이 된다.
이런 신경망들이 많아지면 <code>Deep Learning</code>이 되는 것이다.</p>
<p>아래 3가지(회귀, 단일 분류, 다중 분류) 문제를 실습해보며 맛 보자!!</p>
<br>

<h2 id="회귀---age-weight를-통한-blood-fit-예측">회귀 - Age, Weight를 통한 Blood fit 예측</h2>
<p>📌 데이터 준비</p>
<pre><code class="language-python">raw_data_link = &#39;https://raw.githubusercontent.com/PinkWink/ML_tutorial/master/dataset/x09.txt&#39;
raw_data = np.genfromtxt(raw_data_link, skip_header=36)
# 1열은 인덱스, 2열의 1들은 구분선 표시, 3열 ~ 5열까지가 실제로 들어있는 데이터이다.</code></pre>
<p>현재 raw_data에서 얻고자 하는 것은 Age, Weight 값을 주었을 때 Blood fat을 예측하는 것이다.</p>
<p>Linear Regression 모델로 이를 풀어보자.</p>
<p>📌 데이터 분리</p>
<pre><code class="language-python"># Train
x_data = np.array(raw_data[:, 2:4], dtype = np.float32)
y_data = np.array(raw_data[:,4], dtype = np.float32) # (25, )

y_data = y_data.reshape((25, 1)) # (25, )은 numpy 연산이 되지 않으므로 (25, 1)로 reshape</code></pre>
<p>Train에 사용할 데이터를 분리해주었다.</p>
<p>이제 모델링을 할 것인데 머신러닝 때와 비슷하게 
<strong>모델 구성 -&gt; 학습 -&gt; 예측 순으로 진행</strong>된다. </p>
<p><strong>📌 모델 구성</strong>
<img src="https://velog.velcdn.com/images/castle_mi/post/1a4a3b2e-c235-4ce0-91f3-7b8a80951a6c/image.png" alt=""></p>
<pre><code class="language-python"># 모델링
import tensorflow as tf

model = tf.keras.models.Sequential([
    tf.keras.layers.Dense(1, input_shape = (2, )), # 얻고자 하는 입력 shape이 2개, bias 1개
])
model.compile(optimizer = &#39;rmsprop&#39;, loss = &#39;mse&#39;) # mse를 기준으로 최적화하는 가중치 반환</code></pre>
<p>위 사진과 같은 구성을 가진 모델을 코드로 짜면 위와 같다. </p>
<p>이때 <code>loss 함수</code>란, </p>
<p>학습을 위해서 정해주어야하는 함수로, 정답까지 얼마나 멀리 있는지를 측정할 수 있는 함수이다.
loss 함수를 설정하고 optimizer를 설정하는 방식인데, <code>optimizer</code>는 loss를 어떻게 줄일 것인지를 결정하는 방법을 선택하는 것이다.</p>
<p>실습을 하면서 이에 대해 더 알아보자.</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/5121ed58-9413-462a-bdec-a30dc9cae05e/image.png" alt=""></p>
<p><code>summary()</code>를 통해 현재 모델이 어떻게 구성이 되어있는지 확인도 가능하다.</p>
<br>

<p><strong>📌 학습</strong></p>
<pre><code class="language-python"># 학습
hist = model.fit(x_data, y_data, epochs=5000)</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/802aa0aa-85f2-4fdc-8ea0-1909b73342e8/image.png" alt=""></p>
<p>epoch이 늘어남에 따라 loss값이 줄어드는 것을 볼 수 있다. </p>
<p><code>history()</code>를 통해 아래와 같이 시각적으로도 loss값이 떨어지는 것을 확인할 수 있다. </p>
<pre><code class="language-python">plt.plot(hist.history[&#39;loss&#39;])
plt.title(&#39;Loss Value by epochs&#39;)</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/6797deee-9873-45eb-bc3d-08e12aaff14e/image.png" alt=""></p>
<p>한 3천번 정도까지만 학습했어도 충분했을 것으로 보인다.</p>
<br>

<p><strong>📌 예측</strong></p>
<p>이제 학습된 모델을 이용하여 예측을 해보자. 머신러닝 때와 같이 <code>predict</code>를 이용해주면 된다. </p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/1523b396-240f-493f-8abc-311ea4f3f655/image.png" alt=""></p>
<p>추가로, 학습된 가중치와 bias도 아래와 같이 확인할 수 있다.</p>
<pre><code class="language-python">W_, b_ = model.get_weights()
W_, b_</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>(array([[1.2490779],
        [5.57116  ]], dtype=float32),
 array([4.977017], dtype=float32))</p>
<p><br><br></p>
<h2 id="단일-분류---xor-문제">단일 분류 - XOR 문제</h2>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/8cd5102b-81cb-47a1-a654-bd926ce0bfea/image.png" alt="">
XOR 문제는 앞서 직선 하나로 해결할 수 있었던 것과 달리, 절대 선형 문제로 풀 수 없다. 즉, 뉴런 하나로 문제를 해결할 수 없고 레이어를 2개 이상 쌓아야한다는 뜻이 된다. </p>
<br>

<p>📌 데이터 준비</p>
<pre><code class="language-python"># 데이터 준비
X = np.array([[0,0],
              [1,0],
              [0,1],
              [1,1]])
y = np.array([[0], [1], [1], [0]])</code></pre>
<p>간단하게 데이터를 준비하고 모델링을 해보자.</p>
<br>

<p><strong>📌 모델 구성</strong>
<img src="https://velog.velcdn.com/images/castle_mi/post/ef1e41d7-0201-417d-a181-0a38cedc0a78/image.png" alt=""></p>
<pre><code class="language-python"># 모델링
model = tf.keras.Sequential([
    # 입력을 2개 받아 각각 출력한 다음
    tf.keras.layers.Dense(2, activation=&#39;sigmoid&#39;, input_shape = (2, )),
    # 출력된 결과를 하나로 모아줌
    tf.keras.layers.Dense(1, activation=&#39;sigmoid&#39;)
])</code></pre>
<p><br><br></p>
<h3 id="activation-function---sigmoid">activation function - &#39;sigmoid&#39;</h3>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/1fbf47a0-4de7-40d8-b733-0a16934b6121/image.png" alt=""></p>
<p>이때 activation 함수로 <code>sigmoid</code>를 설정한 이유는 <em>비선형 함수를 같이 통과시켜 주는 데에 의의가 있다.</em>
디폴트 설정은 선형 함수(직선)이므로 이를 두 번 통과시키면 결국엔 선형 함수가 되어버려 우리가 기존에 직선 하나로 해결할 수 없어 레이어를 2번 쌓은 효과가 사라진다.
따라서 선형이 되지 않도록 activation 함수를 비선형 함수인 <code>sigmoid</code>를 설정해준 것이다.</p>
<pre><code class="language-python"># 확률적으로 샘플링하여 학습시킨 후 계산하라는 코드
model.compile(optimizer=tf.keras.optimizers.SGD(lr = 0.1), loss = &#39;mse&#39;)
model.summary() # 모델 구성 확인</code></pre>
<p><code>optimizer</code>로는 <code>SGD</code>를 사용했는데 이와 관련된 내용은 <code>iris</code> 데이터를 실습하며 더 자세히 설명하도록 하겠다. </p>
<br>

<p><strong>📌 학습 및 예측</strong></p>
<pre><code class="language-python"># 학습
# epochs : 지정된 횟수만큼 학습
# batch_size : 한 번의 학습에 사용될 데이터의 개수
hist = model.fit(X, y, epochs=5000, batch_size=1) 


# 예측
model.predict(X)</code></pre>
<p>다음에 이야기할 소재이지만 언급만 해놓자면, 레이어가 2개 이상일 때 오차를 어떻게 계산하는지가 딥러닝 세계에서 항상 의문이었다.</p>
<p>맨 마지막 단계에서는 실제값이 있으니 오차를 계산할 수 있지만, <em>중간 단계에서는 실제값이 없기 때문에</em> 오차 계산이 어려웠던 것이다. 이후, <code>역전파</code>라는 개념이 등장하면서 이를 효과적으로 풀 수 있게 되었는데 이 이론은 다음 <code>iris</code> 데이터를 실습 때 더 언급하겠다.</p>
<p><br><br></p>
<h2 id="다중-분류---iris-데이터">다중 분류 - iris 데이터</h2>
<p>📌 데이터 준비</p>
<pre><code class="language-python">from sklearn.datasets import load_iris

iris = load_iris()
X = iris.data
y = iris.target</code></pre>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/028879da-2803-47c3-9b92-723d80549b10/image.png" alt=""></p>
<p>먼저 다중 분류의 값은 위와 같이 클래스로 분류되어있다.</p>
<p>우리는 오차를 <code>실제값 - 예측값</code> 형식으로 두 차이를 계산해주었는데 분류에서는 오차를 이렇게 바로 계산하면 안된다. </p>
<p>오차를 계산할 때 이 형식이 가능하도록 데이터를 변형시켜주어야하는데 이 때 사용하는 것이 <code>원 핫 인코딩</code>이다. </p>
<h3 id="원-핫-인코딩">원 핫 인코딩</h3>
<pre><code class="language-python"># y label 변형
from sklearn.preprocessing import OneHotEncoder

enc = OneHotEncoder(sparse = False, handle_unknown=&#39;ignore&#39;)
enc.fit(y.reshape(len(y), 1))
enc.categories_ # [array([0, 1, 2])]

# 실제 변형
y_onehot = enc.transform(y.reshape(len(y), 1))
y_onehot[:3] # 이런 형태로 변환되었다.</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>array([[1., 0., 0.],
       [1., 0., 0.],
       [1., 0., 0.]])</p>
<br>

<p>📌 데이터 분리</p>
<pre><code class="language-python">from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X, y_onehot, test_size=0.2, random_state=13)</code></pre>
<p>이제 모델을 구성해보자.
모델을 구성하기 전, 앞서 설명했던 <code>역전파</code>와 <code>activation function</code>, <code>optimizer</code>에 대해 알아보며 모델을 구성해보자. </p>
<h3 id="activation-function---relu">activation function - &#39;relu&#39;</h3>
<h4 id="역전파">역전파</h4>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/704ecfb8-89a1-4926-addc-0b5893f0f489/image.png" alt=""></th>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/e17ccf07-a68b-4f07-be8b-05355daace1d/image.png" alt=""></th>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/cbf55d81-4a50-4e2c-8ec8-23efba104ffa/image.png" alt=""></th>
</tr>
</thead>
</table>
<p><code>relu</code>는 레이어가 깊어질수록 gradient vanishing 현상이 생기는 <code>sigmoid</code> 함수를 대신하기 위해 사용하는 것이다.</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/904bda20-b825-4d96-962e-9c147f0110a4/image.png" alt=""></p>
<h3 id="activation-function---softmax">activation function - &#39;softmax&#39;</h3>
<p><a href="https://syj9700.tistory.com/38"><img src="https://velog.velcdn.com/images/castle_mi/post/2577b018-298b-4560-b19c-a31f9b49ed22/image.png" alt=""></a>
🖱️ 사진 클릭시 softmax 설명 링크 이동</p>
<p>그리고 모델을 구성할 때 마지막 <code>activation function</code>은 내가 얻고자하는 대답에 맞는 함수를 선택해야한다.</p>
<p>1️⃣ 
예를 들어, <strong>첫 번째 실습 문제처럼 회귀 예측 같은 경우</strong>에는 해당 output을 그대로 받으면 되기 때문에 아무것도 설정해주지 않았다. </p>
<pre><code class="language-python">model = tf.keras.models.Sequential([
    tf.keras.layers.Dense(1, input_shape = (2, )),
])</code></pre>
<p>2️⃣ 
<strong>두 번째 XOR 문제와 같은 단일 분류 같은 경우</strong>에는 O or X의 값을 반환해주면 되므로 output에 <code>sigmoid</code>를 해주었다. </p>
<pre><code class="language-python">model = tf.keras.Sequential([
    # 입력을 2개 받아 각각 출력한 다음
    tf.keras.layers.Dense(2, activation=&#39;sigmoid&#39;, input_shape = (2, )),
    # 출력된 결과를 하나로 모아줌
    tf.keras.layers.Dense(1, activation=&#39;sigmoid&#39;)
])</code></pre>
<p>3️⃣ 
마지막 <strong>iris 데이터와 같은 다중 분류 문제의 경우</strong>에는 단일 분류처럼 O or X가 아닌 각 클래스에 해당할 확률을 반환해주고 가장 큰 확률을 가지는 클래스를 최종 예측값으로 선택하는 과정이 필요하다. 이 흐름이 진행될 수 있도록 우리는 <code>softmax</code>를 이용해줄 것이다. </p>
<pre><code class="language-python">model = tf.keras.models.Sequential([
    tf.keras.layers.Dense(32, input_shape = (4, ), activation=&#39;relu&#39;),
    tf.keras.layers.Dense(32, activation=&#39;relu&#39;),
    tf.keras.layers.Dense(32, activation=&#39;relu&#39;),
    tf.keras.layers.Dense(3, activation=&#39;softmax&#39;)
])</code></pre>
<br>

<h3 id="optimizer-의-종류">optimizer 의 종류</h3>
<p>이제 loss function을 줄여나가며 compile을 해주어야한다. </p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/3dfbca2c-e938-470e-b466-b39bdf01c437/image.png" alt=""></p>
<p>이때 loss function의 현 가중치에서의 기울기(gradient)를 구해서 loss를 줄이는 방향으로 업데이트를해 나가는데 어떤 방식으로 줄여나가는지를 결정하는 것이 <code>optimizer</code> 이다.</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/6a329f3e-e807-4c25-82a5-1f8cf8a439c4/image.png" alt=""></p>
<p><code>optimizer</code>의 종류는 위와 같이 많이 있다. 데이터의 종류에 따라 어떤 것이 좋은지는 확인해보아야겠지만, 기본적으로 <code>adam</code>을 많이 사용한다고 한다.</p>
<pre><code class="language-python">model.compile(optimizer = &#39;adam&#39;, loss = &#39;categorical_crossentropy&#39;, metrics = [&#39;accuracy&#39;])
model.summary()</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/363436b8-c96c-438f-9f37-445c4850e5f6/image.png" alt=""></p>
<br>

<p><strong>📌 학습 및 평가</strong></p>
<pre><code class="language-python"># 학습
hist = model.fit(X_train, y_train, epochs=100)

# 모델 평가
model.evaluate(X_test, y_test, verbose = 2)</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>[0.09855823218822479, 1.0]</p>
<p>loss와 accurcay도 확인할 수 있다.</p>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/62d6ac34-842c-4da8-88f0-cdea008ea76b/image.png" alt=""> plt.plot(hist.history[&#39;loss&#39;])</th>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/a68ce52d-4cac-4da4-91ad-e028a5aeafb9/image.png" alt=""> plt.plot(hist.history[&#39;accuracy&#39;])</th>
</tr>
</thead>
</table>
<p><br><br><br></p>
<h2 id="다중-분류---mnist">다중 분류 - MNIST</h2>
<p><a href="https://velog.io/@castle_mi/%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-PCAPrincipal-Component-Analysis-iris-wine-eigenface-MNIST-%ED%83%80%EC%9D%B4%ED%83%80%EB%8B%89-%EB%8D%B0%EC%9D%B4%ED%84%B0#:~:text=%EA%B0%99%EC%9D%80%20%EC%9D%98%EB%AF%B8%EB%A5%BC%20%EA%B0%80%EC%A7%84%EB%8B%A4.-,MNIST,-%ED%95%84%EA%B8%B0%EC%B2%B4%20%EC%9D%B8%EC%8B%9D%EC%9D%84%20%EC%9C%84%ED%95%B4"><img src="https://velog.velcdn.com/images/castle_mi/post/353b00d8-bed9-49f3-bc1e-943b644ced8b/image.png" alt=""></a></p>
<p>이전 머신러닝 파트에서 MNIST를 사용해본 적이 있다. </p>
<p>이번에는 딥러닝을 통해 필기체 숫자를 인식해보자. </p>
<p>📌 데이터 준비</p>
<pre><code class="language-python"># 데이터 읽기
mnist = tf.keras.datasets.mnist

(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0 , x_test / 255.0</code></pre>
<p>각 픽셀은 255가 최대값이여서 이를 나눠줌으로써 0 ~ 1사이의 값으로 조정해주었다. 일종의 min-max scaler를 적용한 셈이다.</p>
<p>이때 이전 흐름과 비슷하게 간다면 이 또한 다중 분류 문제이므로 y의 값을 원핫 인코딩을 통해 변환해주어야한다. 하지만 이 과정이 귀찮다면(??) 비슷한 기능을 하는 <code>loss function</code>의 <code>sparse_categorical_crossentropy</code>을 이용하면 된다. </p>
<p>이번 실습에서는 이것을 한 번 이용해보자.</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/57b1f434-6c88-4052-b211-fbe5d43ee0d9/image.png" alt="">
이번 모델은 위와 같은 구성을 가질 것이다. 이에 맞게 모델을 구성하면 아래와 같다.</p>
<p><strong>📌 모델 구성 및 학습</strong></p>
<pre><code class="language-python"># 모델링
model = tf.keras.models.Sequential([
    # x_train[0].shape # (28, 28)
    # (28, 28) 픽셀이 input_shape이 된다. 이를 1열로 쫙 펴준다.
    tf.keras.layers.Flatten(input_shape = (28, 28)),
    # 1000개의 노드를 거치자
    tf.keras.layers.Dense(1000, activation=&#39;relu&#39;),
    # 0 ~ 9사이의 클래스 중 어디에 속할지 확인
    tf.keras.layers.Dense(10, activation=&#39;softmax&#39;),
])

model.compile(optimizer=&#39;adam&#39;, loss = &#39;sparse_categorical_crossentropy&#39;, metrics = [&#39;accuracy&#39;])


hist = model.fit(x_train, y_train, validation_data=(x_test, y_test), epochs=10, batch_size=100, verbose=1)</code></pre>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/2d3adf5b-ac91-42e7-a532-44672ffb8c07/image.png" alt=""></p>
<p>accurcay도 거의 1에 가깝게 나오고 있다. </p>
<p>이 모델을 어떻게 사용할 수 있는지 한 번 살펴보자.</p>
<p>이 모델은 마지막에 <code>softmax</code>를 쓰고 있으므로 각 클래스에 해당하는 확률을 반환해준다.</p>
<pre><code class="language-python"># 예측
predicted_result = model.predict(x_test)
predicted_result[0] # 첫번째 데이터에 대한 predict 값</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>array([1.5260557e-11, 4.0400780e-11, 4.4488035e-10, 1.2108550e-06,
       2.6351195e-14, 3.2343700e-12, 1.9304675e-17, 9.9999881e-01,
       2.2159567e-10, 3.5912335e-09], dtype=float32)</p>
<p>따라서 위와 같이 확률 값이 반환되는 것을 볼 수 있다.</p>
<p>여기서 확률값이 최대가 되는 클래스가 최종 예측값이 되는 것인데, 최대값의 위치를 반환해주는 <code>argmax</code>를 이용하면 편하다.</p>
<pre><code class="language-python">predicted_label = np.argmax(predicted_result, axis = 1)

wrong_result = []
for n in range(0, len(y_test)):
    if predicted_label[n] != y_test[n]:
        wrong_result.append(n) # 틀린 인덱스 
len(wrong_result) # 틀린 값들 개수</code></pre>
<p>위와 같이 y_test와 비교하여 183개의 데이터가 틀렸다는 것을 알 수도 있고 </p>
<pre><code class="language-python">import random 
samples = random.choices(population=wrong_result, k = 16) # 틀린 것들 중 랜덤으로 몇 개만 살펴보자.

plt.figure(figsize=(14, 12))
for idx, n in enumerate(samples):
    plt.subplot(4, 4, idx+1)
    plt.imshow(x_test[n].reshape(28, 28) , cmap = &#39;Greys&#39;)
    plt.title(&#39;Label : &#39; + str(y_test[n]) + &#39; | Predict : &#39; + str(predicted_label[n]))
    plt.axis(&#39;off&#39;)
plt.show()</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/f8563ec3-8350-46f3-9983-082029c12ec6/image.png" alt=""></p>
<p>틀린 것들 중 랜덤으로 골라 실제 라벨과 예측값이 어떻게 다른지도 확인해볼 수 있다.</p>
<blockquote>
<p>MNIST에서는 Fashion 관련 데이터도 있으니 관심있는 사람들은 한 번 해봐도 좋을 것 같다 :)</p>
</blockquote>
<p><br><br><br></p>
<h2 id="cnn">CNN</h2>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/0be6da7f-dea9-4be6-81f0-6266d1b935d0/image.png" alt=""></p>
<p>이전 영상처리를 하는 분야에서는 그림에 블러 처리를 한다던가 아니면 그림을 흑과 백의 그림으로 바꾸는 등의 필터를 적용하는 작업을 할 때에 픽셀 값의 변화(=미분값)를 계산하여 그 값의 변동이 심한 지점을 특징으로 잡아내는 작업들을 했었다.</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/d12971ee-f4c3-486c-8daa-15d0c57427c7/image.png" alt=""></p>
<p>이런 작업들을 딥러닝에서 하기 위하여 미분값 즉 특징들을 계산해주는 파트와 이를 이용하여 클래스로 분류하는 분류 파트로 나누어진 작업이 바로 CNN이다. </p>
<h3 id="convolution-layer--패턴들을-쌓아가며-점차-복잡한-패턴을-인식한다">Convolution Layer : 패턴들을 쌓아가며 점차 복잡한 패턴을 인식한다</h3>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/b966689f-41e7-4a31-8b2a-89e2354a26f9/image.png" alt=""></th>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/7021ce65-29ff-4a1f-b0e4-6ad1f92b91b2/image.png" alt=""></th>
</tr>
</thead>
</table>
<p>이때 계산된 필터를 <code>Convolutional Filter</code>라고 하는데 이를 직접 구할 수도, 학습을 통해 구할 수도 있다. </p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/c28b59cc-5864-4481-9889-65a233c8212f/image.png" alt=""></p>
<p>또한 이렇게 계산된 필터는 가장자리에 있는 데이터들이 짤리는 경우가 많아 사이즈를 유지하기 위해 conv 진행 전에 0을 모서리에 채우고 계산을 하는 <code>zero padding</code> 도 있다.</p>
<h3 id="pooling--사이즈를-줄여가며-더욱-추상화-해나간다">Pooling : 사이즈를 줄여가며, 더욱 추상화 해나간다</h3>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/0241142d-a841-4739-9d60-e024175b4515/image.png" alt=""></th>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/c14cb8c1-7569-4745-836d-49ca3a94e65b/image.png" alt=""></th>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/b413a545-c9d8-4309-8086-b97dfbc1a3b8/image.png" alt=""></th>
</tr>
</thead>
</table>
<p>이미지를 너무 확대해서 보면 해당 그림이 무엇인지를 모를 수 밖에 없다. 이런 경우, 이미지와 거리를 두고 좀 멀리서 보면 해당 이미지가 무엇인지 알 수 있다.</p>
<p>딥러닝에게 &quot;좀 멀리서 봐!&quot;라는 메세지를 전달해주는 것이 <code>Pooling</code>이다. </p>
<h4 id="max-pooling">Max Pooling</h4>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/f5355da5-c96d-4927-84db-a3a70240e868/image.png" alt="">
<code>Pooling</code>에도 여러 방법이 있는데 그 중 <code>Max Pooling</code>은 그룹 중 가장 큰 값을 대표값으로 정하여 계산하는 방법이다. </p>
<p>이때 그룹을 선택하는 옵션이 <code>stride</code>인데 해당 그림에서는 2를 선택했기 때문에 2칸 건너 뛰어서 다음 그룹이 결정된 것이다. </p>
<h3 id="dropout">Dropout</h3>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/b0d5f0fa-0439-4564-a78f-8af05949cad2/image.png" alt="">
<code>Dropout</code>은 모델에게 융통성을 좀 기르게 해주는 방법으로 학습을 시킬 때 일부러 정보를 누락시키거나, 중간 중간 노드를 끄는 것을 말한다. </p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/0710c1c5-e31b-48d8-8d3b-ffe3a2a3ed14/image.png" alt="">
이런 과정을 거쳐 응용력을 길러주는 것이다. </p>
<br>


<p><img src="https://velog.velcdn.com/images/castle_mi/post/70c8f36c-7a54-4ff7-a6b0-447c279538dc/image.png" alt=""></p>
<p>그렇다면 이제 위 모델을 한 번 코드로 짜보자.</p>
<pre><code class="language-python"># 데이터 준비
mnist = tf.keras.datasets.mnist

(X_train, y_train), (X_test, y_test) = mnist.load_data()
X_train, X_test = X_train / 255.0 , X_test / 255.0


# channel이 들어가므로 해당 차원과 맞춰주기 위한 reshape
# 기존 shape ((60000, 28, 28), (10000, 28, 28))
X_train = X_train.reshape((60000, 28, 28, 1))
X_test = X_test.reshape((10000, 28, 28, 1))


# 모델링
from tensorflow.keras import layers, models

model = models.Sequential([
    layers.Conv2D(32, kernel_size = (5, 5), strides = (1, 1), padding = &#39;same&#39;, activation = &#39;relu&#39;,
                  input_shape = (28, 28, 1)),
    layers.MaxPool2D(pool_size = (2, 2), strides = (2, 2)),
    layers.Conv2D(64, (2, 2), activation = &#39;relu&#39;, padding = &#39;same&#39;),
    layers.MaxPool2D(pool_size = (2, 2), strides = (2, 2)),
    layers.Dropout(0.25),
    layers.Flatten(),
    layers.Dense(1000, activation = &#39;relu&#39;),
    layers.Dense(10, activation = &#39;softmax&#39;)

])

model.compile(optimizer = &#39;adam&#39;, loss = &#39;sparse_categorical_crossentropy&#39;, metrics = [&#39;accuracy&#39;])

# 학습
hist = model.fit(X_train, y_train, epochs=5, verbose = 1, validation_data=(X_test, y_test))

# 모델 평가
score = model.evaluate(X_test, y_test) # loss: 0.0360 - accuracy: 0.9894</code></pre>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/90c82d4c-434f-4d9c-897c-628ba1a39297/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[머신러닝] 네이버 책 가격 회귀 예측 / 클러스터링]]></title>
            <link>https://velog.io/@castle_mi/%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-PCA%EB%84%A4%EC%9D%B4%EB%B2%84-%EC%B1%85-%EA%B0%80%EA%B2%A9-%ED%9A%8C%EA%B7%80-%EC%98%88%EC%B8%A1</link>
            <guid>https://velog.io/@castle_mi/%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-PCA%EB%84%A4%EC%9D%B4%EB%B2%84-%EC%B1%85-%EA%B0%80%EA%B2%A9-%ED%9A%8C%EA%B7%80-%EC%98%88%EC%B8%A1</guid>
            <pubDate>Thu, 05 Oct 2023 10:17:08 GMT</pubDate>
            <description><![CDATA[<p>✍🏻 5일 공부 이야기.</p>
<p><a href="https://github.com/castlemi99/ZeroBaseDataSchool/blob/main/Machine%20Learning/12_1.%20%EB%84%A4%EC%9D%B4%EB%B2%84%20%EC%B1%85%20%EA%B0%80%EA%B2%A9%20%ED%9A%8C%EA%B7%80%20%EC%98%88%EC%B8%A1%20%2C%20%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EB%A7%81.ipynb"><img src="https://velog.velcdn.com/images/castle_mi/post/15377d2b-27ca-459b-ac13-59ee13bdc40a/image.png" alt=""></a></p>
<p>오늘 공부한 실습 내용은 위 깃허브 사진을 클릭하면 이동합니다 :)</p>
<p><br><br></p>
<h2 id="네이버-책-가격-회귀-예측">네이버 책 가격 회귀 예측</h2>
<p><a href="https://velog.io/@castle_mi/EDA-Chapter06.-%EC%8B%9C%EA%B3%84%EC%97%B4-%EB%B6%84%EC%84%9DProphet-Chapter07.-Naver-API"><img src="https://velog.velcdn.com/images/castle_mi/post/eae24320-af36-4acc-b924-1820dcab1c52/image.png" alt=""></a></p>
<p>우리는 이전, 네이버 API를 통해 상품을 검색하는 코드를 만들었었다.</p>
<p>이를 이용해 <strong>&quot;네이버에 &lt;파이썬&gt;을 검색하여 나오는 도서들의 가격을 예측해보자.&quot;</strong></p>
<p>이 문제는 그러면 &lt;회귀&gt; 문제가 될 것이다.</p>
<p>다른 함수는 이전에 만들어둔 함수와 똑같은데 <code>get_fields</code> 함수만 이번에 활용될 함수로 조금 변형시켜 주고, <code>get_search_url</code>, <code>get_result_onpage</code>, <code>delete_tag</code> 함수를 사용했다.</p>
<pre><code class="language-python"># 한 페이지의 내용을 pandas로 반환해주는 함수
def get_fields(json_data):
    title = [delete_tag(each[&quot;title&quot;]) for each in json_data[&quot;items&quot;]]
    link = [each[&quot;link&quot;] for each in json_data[&quot;items&quot;]]
    ## 수정된 부분
    price = [each[&quot;discount&quot;] for each in json_data[&quot;items&quot;]]
    publisher = [each[&quot;publisher&quot;] for each in json_data[&quot;items&quot;]]
    isbn = [each[&quot;isbn&quot;].split() for each in json_data[&quot;items&quot;]]

    result_pd = pd.DataFrame({
        &quot;title&quot; : title,
        &quot;price&quot; : price,
        &quot;isbn&quot; : isbn,
        &quot;link&quot; : link,
        &quot;publisher&quot; : publisher

    }, columns = [&quot;title&quot;, &quot;price&quot;, &quot;isbn&quot;, &quot;link&quot;, &quot;publisher&quot;])

    return result_pd</code></pre>
<p>그리고 1000개의 데이터를 수집해주고 정리해주었다.</p>
<pre><code class="language-python"># API를 이용한 1000개의 데이터 수집
result_book = []

for n in range(1, 1000, 100):
    url = get_search_url(&#39;book&#39;, &#39;파이썬&#39;, n,100)
    json_result = get_result_onpage(url)
    pd_result = get_fields(json_result)

    result_book.append(pd_result)

result_book = pd.concat(result_book)

# 인덱스 정리
result_book.reset_index(drop = True, inplace = True)

# 가격 데이터형 정리
result_book[&#39;price&#39;] = result_book[&#39;price&#39;].astype(&#39;float&#39;)</code></pre>
<br>

<p><img src="https://velog.velcdn.com/images/castle_mi/post/1f41240c-6f8f-438d-841e-95f138800153/image.png" alt=""></p>
<p>그리고 책 쪽수 정보도 얻어와보자. </p>
<pre><code class="language-python">import re

tmp = soup.find_all(class_ = &#39;bookBasicInfo_spec__qmQ_N&#39;)[0].get_text()
result = re.search(r&#39;\d+&#39;, tmp) # 숫자 정보만 추출
result.group() </code></pre>
<p>위와 같이 클래스명을 넣어주고 숫자 정보만 추출해주는 정규표현식을 사용해주면 <code>&#39;620&#39;</code>만 얻어올 수 있다. </p>
<p>이를 모든 데이터에 적용시켜보자.</p>
<pre><code class="language-python"># 페이지 정보를 얻기 위한 함수

def get_page_num(soup):
    tmp = soup.find_all(class_ = &#39;bookBasicInfo_spec__qmQ_N&#39;)[0].get_text()

    # 페이지가 동작하지 않거나, 페이지 정보가 없는 경우가 생길 수 있으므로 예외 처리를 진행함
    try : 
        result = re.search(r&#39;\d+&#39;, tmp) # 숫자 정보만 추출
        return result.group() 
    except:
        print(&quot;====&gt; Error in get_page_num&quot;)
        return np.nan


# 전체 데이터에 대해 실시
import time

page_num_col = [] # 페이지 데이터가 담길 변수

for url in result_book[&#39;link&#39;]:
    print(url)
    print(time.time())

    try:
        page_num = get_page_num(BeautifulSoup(urlopen(url), &#39;html.parser&#39;))
        page_num_col.append(page_num)
    # urlopen이 되지 않은 경우를 대비하여 예외 처리 실행
    except:
        print(&quot;===&gt; Error in urlopen&quot;)
        page_num_col.append(np.nan)

    print(len(page_num_col))
    time.sleep(0.5)

result_book[&#39;page_num&#39;] = page_num_col</code></pre>
<p>중간에 페이지 정보가 없거나, urlopen이 되지 않아 에러가 났을 때 중단되지 않고 계속 실행되도록 예외처리도 꼼꼼하게 해주었다. </p>
<p>(지금 생각해보면 get_page_num 함수를 호출했을 때 페이지 정보가 없어 <code>tmp</code> 변수가 불러지지 않을 때도 <code>&quot;===&gt; Error in urlopen&quot;</code> 예외처리로 빠진 것 같다.)</p>
<pre><code class="language-python"># 데이터 형 변환
result_book[&#39;page_num&#39;] = result_book[&#39;page_num&#39;].astype(&#39;float&#39;)

for idx, row in result_book.iterrows():
    if np.isnan(row[&#39;page_num&#39;]):
        print(&quot;Start fix..&quot;)
        print(row[&#39;link&#39;])
        page_num = get_page_num(BeautifulSoup(urlopen(row[&#39;link&#39;]), &#39;html.parser&#39;))
        result_book.loc[idx, &#39;page_num&#39;] = page_num
        time.sleep(0.5)</code></pre>
<p><code>page_num</code> 컬럼을 숫자형으로 바꿔주고 <code>np.nan</code>이 입력된 데이터들에 대해 한 번 더 입력해보라고 코드를 실행시켰는데 <code>IndexError: list index out of range</code> 에러가 떴다. <code>np.nan</code>으로 입력된 데이터들은 모두 페이지 정보 데이터가 없어 <code>tmp</code> 변수를 찾을 수 없어 위 에러가 떴던 것 같다.</p>
<p>그래서 만약 위에서 <code>get_page_num</code> 함수에서는 페이지 정보가 없었을 때의 예외처리를 넣고 전체 페이지 데이터를 입력하는 코드에서는 <code>urlopen</code>이 되지 않을 때의 예외처리를 하고 싶다면 아래와 같이 <code>get_page_num</code> 함수 코드를 수정하는 것이 맞는 것 같다.</p>
<pre><code class="language-python">def get_page_num(soup):
    tmp = soup.find_all(class_=&#39;bookBasicInfo_spec__qmQ_N&#39;)

    # 페이지 정보가 있는 경우
    if len(tmp) &gt; 0:
        try:
            result = re.search(r&#39;\d+&#39;, tmp[0].get_text()) # 숫자 정보만 추출
            return result.group()
        except:
            print(&quot;====&gt; Error in get_page_num&quot;)
            return np.nan
    # 페이지 정보가 없는 경우
    else:
        print(&quot;====&gt; &#39;bookBasicInfo_spec__qmQ_N&#39; class not found&quot;)
        return np.nan</code></pre>
<pre><code class="language-python"># 다시 시도했지만 입력이 안된 책 정보들은 삭제
result_book = result_book[result_book[&#39;page_num&#39;].notnull()]


# 엑셀로 정리
writer = pd.ExcelWriter(&quot;./data/python_books.xlsx&quot; , engine = &#39;xlsxwriter&#39;)
result_book.to_excel(writer, sheet_name =&#39;Sheet1&#39;)

workbook = writer.book
workbook = writer.sheets[&#39;Sheet1&#39;]
workbook.set_column(&quot;A:A&quot;, 5)
workbook.set_column(&quot;B:B&quot;, 60)
workbook.set_column(&quot;C:C&quot;, 10)
workbook.set_column(&quot;D:D&quot;, 15)
workbook.set_column(&quot;E:E&quot;, 10)
workbook.set_column(&quot;F:F&quot;, 50)

writer.save()</code></pre>
<p>가격 정보가 없는 데이터는 삭제하고 엑셀로 해당 데이터프레임을 저장해주었다.</p>
<p><br><br></p>
<p>이제부터 진짜 분석이다 ㅎㅎ^^</p>
<pre><code class="language-python">raw_data = pd.read_excel(&#39;./data/python_books.xlsx&#39;, index_col = 0)


import seaborn as sns
import matplotlib.pyplot as plt

plt.figure(figsize=(12, 8))
sns.regplot(x = &#39;page_num&#39;, y = &#39;price&#39;, data = raw_data)
plt.show()</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/2af9e9b0-8352-4ee9-9f97-158de77da786/image.png" alt=""></p>
<p>우리의 목적은 책의 가격을 예측하는 것이므로 <code>page_num</code>과 <code>price</code> 사이의 <code>regplot</code>을 그려보았다. 은근 둘의 상관성이 있는 것 같다.</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/9f4b1541-d073-408f-9d71-76a961aa1acc/image.png" alt=""></p>
<p>출판사들의 분포 편차가 너무 심해서 일부 상위 4개의 출판사들의 <code>page_num</code>과 <code>price</code> 사이의 <code>regplot</code>도 그려보았다.</p>
<pre><code class="language-python">import seaborn as sns

# 출판사별 가격 회귀 시각화 함수
def show_regplot_pub(publisher, ax):
    raw_1 = raw_data[raw_data[&#39;publisher&#39;] == publisher]
    sns.regplot(x=&#39;page_num&#39;, y=&#39;price&#39;, data=raw_1, ax=ax)

fig, ax = plt.subplots(1, 4, figsize=(20, 8))

show_regplot_pub(&#39;에이콘출판&#39;, ax[0])
show_regplot_pub(&#39;한빛미디어&#39;, ax[1])
show_regplot_pub(&#39;위키북스&#39;, ax[2])
show_regplot_pub(&#39;비제이퍼블릭&#39;, ax[3])

plt.show()</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/3370f03f-009c-4350-92af-71856ad4338d/image.png" alt=""></p>
<p>출판사별 페이지 개수에 따른 가격을 예측하는 것도 나쁘지 않아보인다.</p>
<pre><code class="language-python"># 데이터 분리(페이지 정보 - 가격 회귀 예측)
from sklearn.model_selection import train_test_split

X = raw_data[&#39;page_num&#39;].values
y = raw_data[&#39;price&#39;].values

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=13)
X_train = X_train.reshape(-1,1)
X_test = X_test.reshape(-1,1)


# 모델 학습
from sklearn.linear_model import LinearRegression

reg = LinearRegression()
reg.fit(X_train, y_train)


# 에러 계산
from sklearn.metrics import mean_squared_error

pred_tr = reg.predict(X_train)
pred_test = reg.predict(X_test)

rmse_tr = (np.sqrt(mean_squared_error(y_train, pred_tr)))
rmse_test = (np.sqrt(mean_squared_error(y_test, pred_test)))

print(&quot;RMSE of Train Data : &quot;, rmse_tr)
print(&quot;RMSE of Test Data : &quot;, rmse_test)</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>RMSE of Train Data :  9265.243542372275
RMSE of Test Data :  7447.320295153908</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/46e9a235-67ba-4aae-852d-bed8668d1687/image.png" alt=""></p>
<p>회귀 문제에서 모델의 성능을 평가하는 것 중  <code>RMSE</code>가 있다. 실제값과 예측값을 시각화한 것을 보아하니, 실제값보다 싸게 예측한 것이 많아보인다.</p>
<br>


<p>이번에는 출판사별 가격 예측을 해보자.</p>
<p>앞서 시각화된 출판사 중 회귀로 잘 표현된 &quot;비제이퍼블릭&quot; 출판사의 가격을 예측해보자.</p>
<pre><code class="language-python">raw_1 = raw_data[raw_data[&#39;publisher&#39;] == &quot;비제이퍼블릭&quot;]

X = raw_1[&#39;page_num&#39;].values
y = raw_1[&#39;price&#39;].values</code></pre>
<p>&quot;비제이퍼블릭&quot; 출판사 데이터만 추출하여 위와 똑같이 코드를 실행시켜주면 </p>
<blockquote>
<p> 💻 출력</p>
</blockquote>
<p>RMSE of Train Data :  2203.4514471499165
RMSE of Test Data :  7685.712498848275</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/2c77a4ed-f641-4944-bde3-16bfaaf9bbce/image.png" alt=""></p>
<p>아래와 같은 결과를 얻을 수 있다.</p>
<p>데이터의 수가 적어서 성능이 좋아보인 걸수도 있지만 앞의 결과보단 성능이 좋아보이긴 한다 :)</p>
<p><br><br></p>
<h1 id="클러스터링">클러스터링</h1>
<pre><code class="language-python">from sklearn.cluster import KMeans</code></pre>
<p>문장의 유사도, PCA 등 우리는 앞서 비지도 학습을 배웠다. 비지도 학습의 다른 모델 중 하나가 <strong>클러스터링</strong>이다.</p>
<p><strong>군집화</strong>라고도 불리는 클러스터링이란, 비슷한 샘플들의 모임이라 생각하면 된다.</p>
<p>클러스터링 중 가장 일반적인 알고리즘은 <strong>K-Means</strong>이다. 임의의 지점을 선택해서 해당 중심(= KMeans에는 평균을 의미)에 가장 가까운 포인트들을 선택하는 군집화이다. 거리 기반 알고리즘이라 속성의 개수가 많을 경우 계산량도 커지고 정확도가 많이 떨어지는 단점이 있다.</p>
<p>몇 개의 군집으로 나눌 것인지에 대한 선택은 <code>n_clusters</code> 옵션을 통해 우리가 설정해주어야하는 하이퍼파라미터이다.</p>
<h2 id="iris">iris</h2>
<p>앞서 여러번 실습했던 <code>iris</code> 데이터에서 실습해보자.</p>
<p>우리는 여러 번의 실습을 통해 petal 특성들이 유의미하다는 것을 알기 때문에 데이터를 읽고 편의상 petal 컬럼만 추출했다. </p>
<pre><code class="language-python">feature = iris_df[[&#39;petal length&#39;, &#39;petal width&#39;]]


# 군집화
model = KMeans(n_clusters = 3) # 몇 개로 군집화 할건지는 우리가 정해줘야함
# 아래와 같은 옵션들이 더 있다.
# init : 초기 군집 중심점의 좌표를 설정하는 방식
# max_iter : 최대 반복횟수, 모든 데이터의 중심점 이동이 없으면 종료
model.fit(feature)</code></pre>
<p>fit을 시켜주면 아래와 같은 정보를 얻을 수 있다.</p>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/1357dfc1-ab3c-48b4-99f9-e16b69d09f8d/image.png" alt=""></th>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/6ea02f3a-e5e0-49e4-91f1-1673a7f672f9/image.png" alt=""></th>
</tr>
</thead>
</table>
<p>💡 여기서 주의해야할 것이 있다.</p>
<p>지금 이것들은 비지도 학습이기 때문에 우리가 익히 알고있는 iris 의 라벨의 순서와는 다르다. <code>labels_</code>에 출력되는 것은 군집화된 특정 라벨이지 본래 타겟 데이터의 라벨과는 상관 없음을 주의하자.</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/2430d093-03d8-43a4-a97f-aba7962e2674/image.png" alt=""></p>
<p>데이터들이 군집된 것을 시각화해보면 아주 이쁘게 잘 된 것 같다.</p>
<br>

<h2 id="make_blobs">make_blobs</h2>
<p>다음으론 클러스터링을 공부하기 좋은 데이터인 <code>make_blobs</code>을 실습해보자.</p>
<pre><code class="language-python">from sklearn.datasets import make_blobs

X, y = make_blobs(n_samples= 200, # 200개의 데이터
                  n_features=2,   # 2개의 피쳐
                  centers = 3,    # 3개의 중심값
                  cluster_std=0.8,# 데이터들의 표준편차
                  random_state=0
                  )
print(X.shape, y.shape)

# y의 값들과 각 값들의 개수 출력
unique, counts = np.unique(y, return_counts = True)
print(unique, counts)</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>(200, 2) (200,)
[0 1 2] [67 67 66]</p>
<p>위와 같이 데이터를 읽어주면 설정한 값에 따라 200개의 데이터가 0 : 67개, 1 : 67개, 2 : 66개가 있는 것을 볼 수 있다.</p>
<p>데이터를 정리해주고 군집화를 실시하여 시각화하면 아래와 같은 결과를 볼 수 있다.</p>
<pre><code class="language-python"># 결과 시각화
centers = kmeans.cluster_centers_ # 군집 중심값
unique_labels = np.unique(cluster_labels)
markers = [&#39;o&#39;, &#39;s&#39;, &#39;^&#39;, &#39;P&#39;, &quot;D&quot;, &#39;H&#39;, &#39;x&#39;] # 마커 모양

for label in unique_labels:
    label_cluster = cluster_df[cluster_df[&#39;kmeans_label&#39;] == label]
    center_x_y = centers[label]
    # 실제값 표시 및 군집화된 컬러 표시
    plt.scatter(x = label_cluster[&#39;ftr1&#39;], y = label_cluster[&#39;ftr2&#39;],
                edgecolors=&#39;k&#39;, marker = markers[label])

    # 중심값
    # 배경 설정
    plt.scatter(x = center_x_y[0], y = center_x_y[1], s = 200, color = &#39;white&#39;,
                alpha = 0.9, edgecolor = &#39;k&#39;, marker=markers[label])
    # 글씨 설정
    plt.scatter(x = center_x_y[0], y = center_x_y[1], s = 70, 
                color = &#39;k&#39;, edgecolor = &#39;k&#39;, marker = &#39;$%d$&#39; % label)

plt.show()</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/e9e09851-5a60-4dae-b018-7305d03f88ea/image.png" alt=""></p>
<p>잘 분리된 것 같다..!!</p>
<p>하지만, 언제나 시각화의 결과를 보고 성능이 좋아보인다/아니다를 판단할 순 없다. 지도학습은 정확도, recall 등 모델을 평가할 수 있는 기준이 있었는데 <strong>비지도학습 모델은 어떻게 평가해야할까?</strong></p>
<p>비지도 학습을 평가하기 위해 <strong>&lt;실루엣 분석&gt;</strong>을 많이 활용한다.
실루엣 분석이란 각 군집 간의 거리가 얼마나 효율적으로 분리되어있는지를 나타내는 것으로 다른 군집과는 거리가 떨어져있고 동일 군집 간의 데이터는 서로 가깝게 잘 뭉쳐져있는지를 확인할 수 있다.</p>
<h2 id="실루엣-분석">실루엣 분석</h2>
<p><code>pip install yellowbrick</code></p>
<p><a href="https://velog.io/@gangjoo/ML-%EA%B5%B0%EC%A7%91%ED%99%94-%EC%8B%A4%EB%A3%A8%EC%97%A3-%EB%B6%84%EC%84%9D-Silhouette-Analysis">https://velog.io/@gangjoo/ML-%EA%B5%B0%EC%A7%91%ED%99%94-%EC%8B%A4%EB%A3%A8%EC%97%A3-%EB%B6%84%EC%84%9D-Silhouette-Analysis</a></p>
<p>위 분의 링크가 잘 정리된 것 같아 참고용으로 올려두었다!</p>
<p><code>iris</code> 데이터를 군집화한 것을 실루엣 분석으로 시각화하고 싶다면 아래와 같이 코드를 작성할 수 있다.</p>
<pre><code class="language-python">from yellowbrick.cluster import silhouette_visualizer
silhouette_visualizer(kmeans, iris.data, colors = &#39;yellowbrick&#39;)</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/5a519c16-15be-4e6d-8a3f-ab917dfc323a/image.png" alt=""></p>
<p>군집이 잘 분리된 모델일 수록 각 모양간의 간격이 넓고 뭉개지지 않은 형태를 띈다.</p>
<br>

<h2 id="이미지-분할">이미지 분할</h2>
<p><code>KMeans</code>를 통해 이미지를 읽어 해당 이미지 속 색상을 추출하여 이미지를 다시 읽는 것도 가능하다. 딥러닝에 들어가면 더 좋은 성능을 가진 모델을 배울텐데 지금은 단순히 색상을 분할하는 것을 목적으로 간단히 짚고 넘어가보자.</p>
<pre><code># 이미지 읽기
from matplotlib.image import imread

image = imread(&#39;./data/image.jpg&#39;)
plt.imshow(image)</code></pre><blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/63fcfe93-db4f-4bad-bc92-c4a4f38c7aff/image.png" alt=""></p>
<p>나는 위와 같은 그림을 분할시켜보려 한다.</p>
<pre><code class="language-python"># 좀 더 여러가지 군집화를 진행
segmented_imgs = []
n_colors = [10,8,6,4,2]
for n_clusters in n_colors:
    kmeans = KMeans(n_clusters=n_clusters, random_state=13).fit(X)
    segmented_img = kmeans.cluster_centers_[kmeans.labels_].astype(int) 
    segmented_imgs.append(segmented_img.reshape(image.shape))


# 각 cluster 별 그림
plt.figure(figsize=(10,5))
plt.subplots_adjust(wspace=0.05,hspace = 0.1)

plt.subplot(231)
plt.imshow(image)
plt.title(&quot;Original image&quot;)
plt.axis(&#39;off&#39;)

for idx, n_clusters in enumerate(n_colors):
    plt.subplot(232 + idx)
    plt.imshow(segmented_imgs[idx])
    plt.title(&quot;{} colors&quot;.format(n_clusters))
    plt.axis(&#39;off&#39;)

plt.show()</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/9e98e67e-39b1-46a0-a0c1-07853d71cf11/image.png" alt=""></p>
<p>추출된 색상의 개수에 따라 그림이 표현되는 것이 달라지는 것을 볼 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[머신러닝] PCA(Principal Component Analysis /  iris, wine, eigenface, HAR, MNIST, 타이타닉 데이터 )]]></title>
            <link>https://velog.io/@castle_mi/%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-PCAPrincipal-Component-Analysis-iris-wine-eigenface-MNIST-%ED%83%80%EC%9D%B4%ED%83%80%EB%8B%89-%EB%8D%B0%EC%9D%B4%ED%84%B0</link>
            <guid>https://velog.io/@castle_mi/%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-PCAPrincipal-Component-Analysis-iris-wine-eigenface-MNIST-%ED%83%80%EC%9D%B4%ED%83%80%EB%8B%89-%EB%8D%B0%EC%9D%B4%ED%84%B0</guid>
            <pubDate>Thu, 05 Oct 2023 09:35:54 GMT</pubDate>
            <description><![CDATA[<p>✍🏻 3일, 4일 공부 이야기.</p>
<p><a href="https://github.com/castlemi99/ZeroBaseDataSchool/blob/main/Machine%20Learning/12.%20PCA.ipynb"><img src="https://velog.velcdn.com/images/castle_mi/post/2f36f063-a5ea-468a-ad84-1d77b037a48b/image.png" alt=""></a></p>
<p>오늘 실습한 내용은 위 깃허브 사진을 클릭하면 이동됩니다 :)</p>
<br>

<h1 id="pcaprincipal-component-analysis">PCA(Principal Component Analysis)</h1>
<p>PCA(주성분 분석)는 데이터 집합 내에 존재하는 각 데이터의 차이를 가장 잘 나타내주는 요소를 찾아내는 방법이다. 주로 통계 데이터분석(주성분 찾기), 데이터 압축(차원 감소), 노이즈 제거 등의 분야에서 사용되고 있다.
<img src="https://velog.velcdn.com/images/castle_mi/post/f9dd35ed-c3ac-45ce-9357-3fa500471a5f/image.png" alt=""></p>
<p>차원 축소(Dimensionality Reduction)와 변수 추출(Feature Extraction) 기법으로 널리 쓰이고 있는데 PCA(주성분 분석)는 데이터의 분산을 최대한 보존하면서 서로 직교하는 새 축을 찾아 고차원 공간의 표본들을 선형 연관성이 없는 저차원의 공간으로 변환하는 기법이다.</p>
<p>이때 변수 추출(Feature Extraction)은 기존 변수를 조합해 새로운 변수를 만드는 기법으로서 변수 선택(Feature Selectrion)과는 다르다는 것을 알아야한다.</p>
<h2 id="sklearn">sklearn</h2>
<p>(200,2)의 shape을 가지는 난수를 발생시키고 해당 데이터를 2개의 주성분으로 표현해보자.</p>
<pre><code class="language-python"># np.random.rand : 0 - 1 사이 균일 분포에서의 난수
# np.random.randn : 가우시안 표준 정규분포에서의 난수

rng = np.random.RandomState(13)
X = np.dot(rng.rand(2, 2) , rng.randn(2, 200)).T

sns.set_style(&#39;whitegrid&#39;)

plt.scatter(X[:,0], X[:,1])
plt.axis(&#39;equal&#39;) # x축과 y축의 1 간격을 같게 조정</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/2c46802f-ab58-46ed-b59f-e8161a7a8aa3/image.png" alt=""></p>
<p>위와 같은 데이터가 만들어졌다. 모든 데이터를 단 2개의 벡터로 표현하는 것이 주성분 분석이다.</p>
<pre><code class="language-python"># n_components : 표시될 주성분 개수

from sklearn.decomposition import PCA

pca = PCA(n_components= 2, random_state=13)
pca.fit(X)</code></pre>
<p>2개의 주성분으로 학습된 pca 변수에는 다음과 같은 인사이트를 얻어낼 수 있다.</p>
<ul>
<li><p><code>pca.components_</code> 
 <img src="https://velog.velcdn.com/images/castle_mi/post/16e02761-351e-435a-af0c-3d215a7f9e16/image.png" alt="">
 2행의 행렬이 반환되었고, 각 반환된 행은 하나의 주성분이 된다.</p>
</li>
<li><p><code>pca.explained_variance_</code> 
 <img src="https://velog.velcdn.com/images/castle_mi/post/9aee55ba-0db5-4a32-a076-198d20c79c24/image.png" alt="">
: 각 주성분의 설명력</p>
</li>
<li><p><code>pca.explained_variance_ratio_</code>
 <img src="https://velog.velcdn.com/images/castle_mi/post/46894ab9-d675-4ae7-ab8e-d27f9d5598f9/image.png" alt="">
 : 각 주성분의 설명력의 비율</p>
</li>
<li><p><code>pca.mean_</code>
 <img src="https://velog.velcdn.com/images/castle_mi/post/abf6d2fa-805a-4e74-a943-1a45e8612688/image.png" alt="">
 : 주성분의 평균으로 시작점이 됨</p>
</li>
</ul>
<pre><code class="language-python">def draw_vector(v0, v1, ax = None):
    # 그림을 그릴 matplotlib 축 
    ax = ax or plt.gca()
    # 화살표 스타일 설정
    arrowprops = dict(
                    arrowstyle = &#39;-&gt;&#39;, # 화살표 스타일
                    linewidth = 2, # 선 굵기
                    color = &#39;black&#39;, # 색상
                    # 시작점(v0)과 끝점(v1)에서 선 길이를 줄이는 비율
                    shrinkA = 0,
                    shrinkB = 0,

    )
    #          텍스트 없이 그려짐, 점 위치 , 점 위치, 화살표 속성
    ax.annotate(&#39;&#39;, v1, v0, arrowprops = arrowprops)

plt.scatter(X[:, 0], X[:, 1], alpha=0.4)

for length, vector in zip(pca.explained_variance_, pca.components_):
    v = vector * 3 * np.sqrt(length) # 3배는 화살표가 보이기 위해 임의로 설정한 값
    # pca.mean_ : 각 벡터에 대한 시작점
    # pca.mean_ + v : 끝점  
    draw_vector(pca.mean_, pca.mean_ + v)

plt.axis(&#39;equal&#39;)
plt.show()</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/0c631f63-b80c-4481-860a-3dafbe0c70c1/image.png" alt=""></p>
<p>주성분을 그리는 함수를 만들어주고 출력하면 두 화살표를 통해 모든 데이터를 표현하는 주성분이 만들어졌다.</p>
<p>주성분은 기존의 축을 주성분으로 바꾸는 주축 변경도 가능하다.
<img src="https://velog.velcdn.com/images/castle_mi/post/d3c6bf7b-7625-420f-9fc5-e9c4261b581c/image.png" alt=""></p>
<br>

<p>그동안 연습했던 여러 데이터들을 주성분 분석도 해보자.</p>
<h2 id="iris-데이터">iris 데이터</h2>
<pre><code class="language-python">import pandas as pd
from sklearn.datasets import load_iris

iris = load_iris()

iris_pd = pd.DataFrame(iris.data, columns = iris.feature_names)
iris_pd[&#39;species&#39;] = iris.target

sns.pairplot(iris_pd, hue = &#39;species&#39;, height = 3, 
             x_vars = [&#39;sepal length (cm)&#39;, &#39;petal length (cm)&#39;],
             y_vars = [&#39;sepal width (cm)&#39;, &#39;petal width (cm)&#39;])</code></pre>
<p>데이터를 읽고 모든 데이터로 일단 <code>pariplot</code>을 그리면 아래와 같다.</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/7ff85e64-d4b9-47fe-bfda-616fa103c3c0/image.png" alt=""></p>
<p>특성 4개로 각 꽃을 분류해보기엔 어려워보인다.</p>
<p>2개의 주성분으로 특성 4개를 줄여보자.</p>
<p>먼저, PCA에서는 Scaler의 적용이 꽤 큰 효과를 불러일으키기 때문에 <code>StandardScaler</code>를 fit시킨 후 주성분 분석을 실시할 것이다.</p>
<pre><code class="language-python">from sklearn.preprocessing import StandardScaler

iris_ss = StandardScaler().fit_transform(iris.data)</code></pre>
<p>그리고 pca의 결과를 반환하는 함수와 그 결과를 데이터프레임으로 반환하는 함수를 만들어주었다.</p>
<pre><code class="language-python">from sklearn.decomposition import PCA

def get_pca_data(ss_data, n_components = 2):
    pca = PCA(n_components= n_components)
    pca.fit(ss_data)

    return pca.transform(ss_data), pca

def get_pd_from_pca(pca_data, cols = [&#39;pca_component_1&#39;, &#39;pca_component_2&#39;]):
    return(pd.DataFrame(pca_data, columns = cols))</code></pre>
<p>앞서 Scaler를 적용한 데이터에 pca를 적용해보자.</p>
<pre><code class="language-python"># 변환

iris_pca, pca = get_pca_data(iris_ss, 2)
# iris_pca.shape #(150, 2)

iris_pd_pca = get_pd_from_pca(iris_pca)
iris_pd_pca[&#39;species&#39;] = iris.target
iris_pd_pca.head()</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/25b45ca3-4f0a-4373-872c-37a7cc43a3d2/image.png" alt=""></p>
<pre><code class="language-python">sns.pairplot(iris_pd_pca, hue = &#39;species&#39;, height = 5, 
             x_vars = [&#39;pca_component_1&#39;],
             y_vars = [&#39;pca_component_2&#39;])</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/820192fd-4d67-47e6-a1af-02ac6ab61a17/image.png" alt=""></p>
<p>그리고 시각화해보면 4개의 특성으로 그린 pairplot보단, 좀 더 분류하기 쉬워보인다.</p>
<p>2가지의 특성을 이용했을 때의 설명력은 <code>pca.explained_variance_ratio_</code>를 통해 <code>array([0.72962445, 0.22850762])</code>라는 결과값이 나왔다. 머신러닝에 적용하면 어떨까?</p>
<pre><code class="language-python">from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score

def rf_scores(X, y, cv = 5):
    rf = RandomForestClassifier(random_state= 13, n_estimators=100)
    scores_rf = cross_val_score(rf, X, y, scoring = &#39;accuracy&#39;, cv = cv)

    print(&quot;Score : &quot;, np.mean(scores_rf))

# 4가지 특성을 모두 사용한 randomforest
# iris_ss : Standard Scaler만 적용한 iris
print(rf_scores(iris_ss, iris.target))


# 주성분 분석을 적용한 randomforest(2가지 특성 이용)
pca_X = iris_pd_pca[[&#39;pca_component_1&#39;, &#39;pca_component_2&#39;]]
print(rf_scores(pca_X, iris.target))</code></pre>
<blockquote>
<p> 💻 출력</p>
</blockquote>
<p>Score :  0.96
Score :  0.9066666666666666</p>
<p>데이터를 100% 반영한 것이 아니기 떄문에 정확도는 떨어질 수 밖에 없다. 하지만 데이터를 줄여서 머신러닝을 돌릴 수 있다는 인사이트를 얻어가자.</p>
<p><br><br></p>
<h2 id="wine-데이터">wine 데이터</h2>
<p>앞서 wine 데이터는 feature가 여러 개였다. 레드 와인인지, 화이트 와인인지 구분하기 위해 주성분 분석과 머신러닝을 돌려보자.</p>
<p>데이터를 읽고 <code>StandardScaler</code>를 적용시키면 아래와 같다.</p>
<pre><code class="language-python">wine_url = &#39;https://raw.githubusercontent.com/PinkWink/ML_tutorial/master/dataset/wine.csv&#39;

wine = pd.read_csv(wine_url, sep = &#39;,&#39;, index_col=0)

# 와인 색상 분류를 하기 위해 타겟 데이터를 &#39;color&#39; 컬럼으로 정함
wine_y = wine[&#39;color&#39;]
wine_X = wine.drop([&#39;color&#39;], axis = 1)

# StandardScaler 
wine_ss = StandardScaler().fit_transform(wine_X)</code></pre>
<pre><code class="language-python">pca_wine, pca = get_pca_data(wine_ss, n_components=2)
# pca_wine.shape #(6497,2)

pca.explained_variance_ratio_ </code></pre>
<p>wine 데이터는 총 12개의 컬럼을 가지고 있는데, 2개의 주성분으로 줄여보았다. 이 때 설명력은 <code>array([0.25346226, 0.22082117])</code> 가 나왔다. 총 설명력이 50%도 되지 않아 분석으로 적당해 보이진 않았지만, 그래도 시각화해보자.</p>
<pre><code class="language-python">pca_columns = [&#39;pca_component_1&#39;, &#39;pca_component_2&#39;]
pca_wine_pd = pd.DataFrame(pca_wine, columns = pca_columns)
pca_wine_pd[&#39;color&#39;] = wine_y.values

sns.pairplot(pca_wine_pd, hue = &#39;color&#39;, height = 5, 
             x_vars=[&#39;pca_component_1&#39;], y_vars = [&#39;pca_component_2&#39;])</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/d86aa835-de43-4461-a2b3-c3432e9329aa/image.png" alt=""></p>
<p>설명력은 좋지 않았지만, 시각화된 것을 보았을 땐 그리 나빠 보이진 않는다! 한 번 머신러닝을 돌려보자.</p>
<ul>
<li>주성분 분석없이 <code>StandardScaler</code>만 돌린 <code>RandomForest</code><pre><code class="language-python"># StandardScaler만 적용된 원 데이터로 돌린 rf
rf_scores(wine_ss, wine_y)</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
 Score :  0.9935352638124</li>
</ul>
<ul>
<li>2개의 PCA로 변환한 데이터로 돌린 <code>RandomForest</code><pre><code class="language-python"># 2개의 pca로 변환한 데이터로 돌린 rf
pca_X = pca_wine_pd[[&#39;pca_component_1&#39;, &#39;pca_component_2&#39;]]
rf_scores(pca_X, wine_y)</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
 Score :  0.981067803635933</li>
</ul>
<p>머신러닝 성능에는 큰 차이가 없어보인다.
이번에는 주성분 3개로 표현해보자.</p>
<pre><code class="language-python">pca_wine, pca = get_pca_data(wine_ss, n_components=3)
print(pca.explained_variance_ratio_ )

pca_columns = [&#39;pca_component_1&#39;, &#39;pca_component_2&#39;, &#39;pca_component_3&#39;]
pca_wine_pd = pd.DataFrame(pca_wine, columns = pca_columns)
pca_wine_pd[&#39;color&#39;] = wine_y.values

pca_X = pca_wine_pd[[&#39;pca_component_1&#39;, &#39;pca_component_2&#39;, &#39;pca_component_3&#39;]]
rf_scores(pca_X, wine_y)</code></pre>
<blockquote>
<p> 💻 출력</p>
</blockquote>
<p>[0.25346226 0.22082117 0.13679223]
Score :  0.9832236631728548</p>
<p>설명력도 60%로 올랐고 98%의 정확도도 보였다.
3개의 특성으로 12개의 특성을 대신할 수 있게 되었다.</p>
<pre><code class="language-python">import plotly_express as px

fig = px.scatter_3d(pca_wine_pd, x = &#39;pca_component_1&#39;, y = &#39;pca_component_2&#39;, z = &#39;pca_component_3&#39;,
                    color = &#39;color&#39;, symbol = &#39;color&#39;, opacity=0.4)
fig.update_layout(margin = dict(l = 0, r = 0, b = 0, t = 0))
fig.show()</code></pre>
<blockquote>
<p> 💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/4b2b1643-810b-41e7-ac10-dbaed5068e54/image.png" alt=""></p>
<p><br><br></p>
<h2 id="pca-eigenface표정-이미지">PCA eigenface(표정 이미지)</h2>
<p><code>pca eigenface</code> 는 사람들의 얼굴 이미지를 분석하여 표정을 찾아볼 수 있는 PCA 의 재밌는 예시이다.</p>
<p>우리는 여기서 특정 인물(20번째 인물 , olivetti)의 10장 사진만 실습해볼 예정이다.</p>
<pre><code class="language-python">from sklearn.datasets import fetch_olivetti_faces

faces_all = fetch_olivetti_faces()

# 특정 인물 선택
K = 20 # 인덱스가 20인 인물 선택
faces = faces_all.images[faces_all.target == K]
# faces.shape # (10,64,64)</code></pre>
<p>데이터를 읽어들였는데 해당 데이터가 어떻게 생긴 것인지 살펴보자. </p>
<pre><code class="language-python">import matplotlib.pyplot as plt

# 2행 5열로 그림을 그리고자 함
N = 2
M = 5
fig = plt.figure(figsize=(10,5))
plt.subplots_adjust(top = 1, hspace = 0, wspace= 0.05)

for n in range(N*M):
    ax = fig.add_subplot(N, M, n+1)
    ax.imshow(faces[n], cmap = plt.cm.bone)
    ax.grid(False)
    ax.xaxis.set_ticks([])
    ax.yaxis.set_ticks([])

plt.suptitle(&#39;Olivetti&#39;)
plt.tight_layout()
plt.show()</code></pre>
<blockquote>
<p> 💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/00436065-d3ef-4cae-8a7e-800271cd8e4b/image.png" alt=""></p>
<p>여러 표정의 10장의 사진이 들어가있다.
이 사진을 2개의 주성분으로 분리시켜 모든 사진을 표현해보도록 하자.</p>
<pre><code class="language-python">from sklearn.decomposition import PCA

pca = PCA(n_components= 2)

X = faces_all.data[faces_all.target == K] # Olivetti 데이터
W = pca.fit_transform(X)

X_inv = pca.inverse_transform(W)

X.shape, W.shape, X_inv.shape, faces.shape</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>(10, 4096) (10, 2) (10, 4096) (10, 64, 64)</p>
<p>X는 10장의 데이터가 64<em>64 픽셀로 이루어진 데이터로 보인다. *</em>이미지 데이터는 픽셀을 맞춰주는 것이 중요하기 때문에 이렇게 <code>shape</code>을 확인하는 것이 중요하다.
**</p>
<p>주성분 분석한 2개의 특성으로 이미지를 그려보자면 기존의 그림(<code>faces</code>)이 64*64 픽셀로 이루어졌으니 <code>X_inv</code>를 reshape 해준 후 그려주어야한다.</p>
<pre><code class="language-python"># 결과 확인

N = 2
M = 5
fig = plt.figure(figsize=(10,5))
plt.subplots_adjust(top = 1, bottom = 0, hspace = 0, wspace= 0.05)

for n in range(N*M):
    ax = fig.add_subplot(N, M, n+1)
    # 기존 shape으로 돌려줘야함 
    ax.imshow(X_inv[n].reshape(64,64), cmap = plt.cm.bone)
    ax.grid(False)
    ax.xaxis.set_ticks([])
    ax.yaxis.set_ticks([])

plt.suptitle(&#39;PCA result Olivetti&#39;)
plt.tight_layout()
plt.show()</code></pre>
<blockquote>
<p> 💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/98ee8af4-9097-4bea-b68a-fe302d9ca849/image.png" alt=""></p>
<p>사진이 약간 뭉그뜨려진 느낌이 있지만 표정은 대체로 잘 표현된 듯하다. </p>
<p>도대체 <code>component</code> 변수에는 무슨 역할을 하는 것이 저장되어있는 것일까?</p>
<pre><code class="language-python">face_mean = pca.mean_.reshape(64,64)
face_p1 = pca.components_[0].reshape(64,64)
face_p2 = pca.components_[1].reshape(64,64)

# 첫번째 그림
plt.figure(figsize=(12,7))
plt.subplot(131)
plt.imshow(face_mean, cmap = plt.cm.bone)
plt.grid(False); plt.xticks([]); plt.yticks([]); plt.title(&quot;mean&quot;)

# 두번째 그림
plt.subplot(132)
plt.imshow(face_p1, cmap = plt.cm.bone)
plt.grid(False); plt.xticks([]); plt.yticks([]); plt.title(&quot;face_p1&quot;)

# 세번째 그림
plt.subplot(133)
plt.imshow(face_p2, cmap = plt.cm.bone)
plt.grid(False); plt.xticks([]); plt.yticks([]); plt.title(&quot;face_p2&quot;)

plt.show()</code></pre>
<blockquote>
<p> 💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/875bdc66-8802-4ca6-9bd1-0e79d0c11257/image.png" alt=""></p>
<p><code>pca_mean</code> , <code>pca.comonents_[0]</code>, <code>pca.comonents_[1]</code> 변수를 그려보았다. 우리가 가진 10장의 데이터는 <code>face_mean</code>에서 <code>face_p1</code>, <code>face_p2</code>를 더한 것을 통해 모두 표현되는 것이다.</p>
<p>해당 주성분이 얼만큼 표현되는지에 대한 가중치를 설정하고 <code>face_mean</code>에서 해당 가중치를 <code>face_p1</code>에 곱한 값을 더해 10장의 사진을 다시 그려보았다.</p>
<pre><code class="language-python">N = 2
M = 5

# 우리는 10장을 그릴 것이다.
w = np.linspace(-5, 10, N*M) # -5부터 10까지 10개의 숫자

# 첫번째 성분 변화
fig = plt.figure(figsize=(10,5))
plt.subplots_adjust(top = 1, bottom = 0, hspace=0,wspace=0.05)

for n in range(N*M):
    ax = fig.add_subplot(N, M, n+1)
    # face_mean에서 face_p1에 가중치를 곱해서 더한 데이터
    ax.imshow(face_mean + w[n] * face_p1, cmap = plt.cm.bone)
    plt.grid(False); plt.xticks([]); plt.yticks([])
    plt.title(&#39;Weight : &#39; + str(round(w[n])))

plt.tight_layout()
plt.show()</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/f0ed60b9-fbbd-41ea-98dd-c41f55fcb3bc/image.png" alt=""></p>
<pre><code class="language-python"># 두번째 성분 변화
fig = plt.figure(figsize=(10,5))
plt.subplots_adjust(top = 1, bottom = 0, hspace=0,wspace=0.05)

for n in range(N*M):
    ax = fig.add_subplot(N, M, n+1)
    # face_mean에서 face_p2에 가중치를 곱해서 더한 데이터
    ax.imshow(face_mean + w[n] * face_p2, cmap = plt.cm.bone)
    plt.grid(False); plt.xticks([]); plt.yticks([])
    plt.title(&#39;Weight : &#39; + str(round(w[n])))

plt.tight_layout()
plt.show()</code></pre>
<blockquote>
<p> 💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/14314448-246c-4b51-94fb-c239299f0f9e/image.png" alt=""></p>
<p>주성분을 조금씩 변화시켜보니 원본 데이터의 모습이 살짝 보이는 것 같다.</p>
<p>이번에는 두 주성분을 한 번에 표현해보자.</p>
<pre><code class="language-python">nx, ny = (5,5)
x = np.linspace(-5, 8, nx)
y = np.linspace(-5, 8, ny)

# 앞서 추출한 가중치를 meshgrid 형태로 추출
w1, w2 = np.meshgrid(x, y) 

# 이미지와 결합시키기 위한 reshape
w1 = w1.reshape(-1,)
w2 = w2.reshape(-1,)

fig = plt.figure(figsize=(10,5))
plt.subplots_adjust(top = 1, bottom = 0, hspace=0,wspace=0.05)

for n in range(N*M):
    ax = fig.add_subplot(N, M, n+1)
    # face_mean에서 face_p1에 가중치를 곱해서 더한 데이터
    ax.imshow(face_mean + w1[n] * face_p1 + w2[n] * face_p2 , cmap = plt.cm.bone)
    plt.grid(False); plt.xticks([]); plt.yticks([])
    plt.title(str(round(w1[n])) + &#39; , &#39; + str(round(w2[n])))

plt.tight_layout()
plt.show()</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/2509f509-29ee-4f94-9cb4-4791d8dba8e6/image.png" alt=""></p>
<p>두 주성분의 가중치를 변화해가며 그려보았는데 이는 아래와 같은 의미를 가진다.</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/c3382329-cb8d-45c5-99fc-5edfd987d95f/image.png" alt=""></p>
<p><br><br></p>
<h2 id="mnist">MNIST</h2>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/64f7a200-4008-4fef-965d-1cc637fa2596/image.png" alt=""></p>
<p>필기체 인식을 위해 제공된 데이터 NIST에서 숫자 데이터만 모아둔 것이 MNIST 데이터셋이다. 
<a href="https://www.kaggle.com/datasets/oddrationale/mnist-in-csv">
<img src="https://velog.velcdn.com/images/castle_mi/post/681e59f9-2dc2-4554-80a8-c3d77c786d3d/image.png" alt=""></a></p>
<p>캐글에서 CSV 파일을 다운받아 사용해도 되고 라이브러리를 불러서 사용할 수도 있다. </p>
<p>28*28 픽셀의 0 - 9사이의 숫자 이미지와 레이블로 구성되어 있으며, 6만개의 훈련용 셋과 1만개의 실험용 셋으로 구성되어있다.</p>
<pre><code class="language-python">df_train = pd.read_csv(&#39;./data/mnist_train.csv&#39;)
df_test = pd.read_csv(&#39;./data/mnist_test.csv&#39;)

X_train = np.array(df_train.iloc[:, 1:])
y_train = np.array(df_train[&#39;label&#39;])

X_test = np.array(df_test.iloc[:, 1:])
y_test = np.array(df_test[&#39;label&#39;])</code></pre>
<p>데이터를 읽고 분리해 주었다.</p>
<p>먼저 데이터가 어떻게 생겼는지 봐보자.
6만개의 훈련용 데이터 셋 중 16개의 데이터만 랜덤으로 살펴보자.</p>
<pre><code class="language-python">import random
samples = random.choices(population=range(0,60000), k = 16) 

import matplotlib.pyplot as plt
plt.figure(figsize=(14,12))

for idx, n in enumerate(samples):
    plt.subplot(4, 4, idx + 1)
    plt.imshow(X_train[n].reshape(28,28), cmap = &quot;Greys&quot;, interpolation = &#39;nearest&#39;)
    plt.title(y_train[n])

plt.show()</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/5a249bb2-4a22-40d7-8b91-325a26df6f7a/image.png" alt=""></p>
<p>랜덤으로 뽑은 16개의 숫자의 인덱스를 가진 데이터를 뽑아봤다.</p>
<p>이제 필기체 데이터를 넣으면 무슨 숫자인지 알아맞추는 모델을 만들기 위해 PCA와 KNN을 적용시켜보자.</p>
<p>먼저 KNN만 적용시킨 결과는 아래와 같다.</p>
<pre><code class="language-python">from sklearn.neighbors import KNeighborsClassifier

clf = KNeighborsClassifier(n_neighbors= 5)
clf.fit(X_train, y_train)

from sklearn.metrics import accuracy_score

pred = clf.predict(X_test)
accuracy_score(y_test, pred)</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>0.9688</p>
<p>96% 의 꽤 좋은 성능을 보여준다. 하지만 KNN은 모든 거리를 계산해야하기 때문에 시간이 오래 걸릴 수도 있다. 위 코드를 실행시키는 데에 20.5초 정도 소요되었다.</p>
<p>만약 특성이 많은 데이터라면 소요되는 시간은 무한대로 늘어날 것이다. 이를 차원의 저주라 하는데 이때 필요한 것이 차원을 줄여주는 <code>PCA</code>이다.</p>
<pre><code class="language-python">from sklearn.pipeline import Pipeline
from sklearn.decomposition import PCA
from sklearn.model_selection import GridSearchCV, StratifiedKFold

pipe = Pipeline([
    (&#39;pca&#39;, PCA()),
    (&#39;clf&#39;, KNeighborsClassifier())
])

parameters = {
    &#39;pca__n_components&#39; : [2,5,10],
    &#39;clf__n_neighbors&#39; : [5,10,15]
}

kf = StratifiedKFold(n_splits= 5, shuffle= True, random_state=13)
grid = GridSearchCV(pipe, parameters, cv = kf, n_jobs= -1, verbose = 1)
grid.fit(X_train, y_train)

pred = grid.best_estimator_.predict(X_test)
accuracy_score(y_test, pred)</code></pre>
<blockquote>
<p> 💻 출력</p>
</blockquote>
<p>0.9289</p>
<p>성능이 조금 줄었지만 앞서 20.5초가 걸렸던 코드가 2초로 엄청 줄었다.</p>
<pre><code class="language-python"># 결과 확인
def results(y_pred, y_test):
    from sklearn.metrics import classification_report, confusion_matrix
    print(classification_report(y_test, y_pred))

results(grid.predict(X_train), y_train)</code></pre>
<blockquote>
<p> 💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/482ac79f-5bc2-4b6e-99cf-bb68e7b2abd2/image.png" alt=""></p>
<p>실제로 결과가 어떤지 보았는데 대체로 골고루 잘 맞추고 있는 것 같다.</p>
<p>잘못 예측한 데이터도 몇 개 살펴보았는데 그건 깃허브에 올려두었으니 관심있으면 참고하기!!</p>
<p><br><br></p>
<h2 id="타이타닉">타이타닉</h2>
<pre><code class="language-python">titanic_url = &#39;https://raw.githubusercontent.com/PinkWink/ML_tutorial/master/dataset/titanic.xls&#39;

titanic = pd.read_excel(titanic_url)</code></pre>
<p><code>titanic</code> 데이터를 부르고 앞서 분석에서 꽤 중요한 역할을 해주었던 <code>title(신분)</code> 컬럼을 추가하고 <code>[&#39;Miss&#39;, &#39;Rare_m&#39;, &#39;Mr&#39;, &#39;Mrs&#39;, &#39;Rare_f&#39;]</code> 의 데이터로 분류해주었다.</p>
<p>그리고 머신러닝을 돌리기 위해선 문자열 데이터들이 숫자형으로 표현되어야하기 때문에 <code>sex</code> 컬럼과 <code>title</code> 컬럼을 <code>LabelEncoder</code>를 돌려 각각 <code>gender</code>, <code>grade</code> 컬럼에 저장해주었다.</p>
<pre><code class="language-python">from sklearn.preprocessing import LabelEncoder

# gender 컬럼 생성
le_sex = LabelEncoder()
le_sex.fit(titanic[&#39;sex&#39;])
titanic[&#39;gender&#39;] = le_sex.transform(titanic[&#39;sex&#39;])

# grade 컬럼 생성
le_grade = LabelEncoder()
le_grade.fit(titanic[&#39;title&#39;])
titanic[&#39;grade&#39;] = le_grade.transform(titanic[&#39;title&#39;])</code></pre>
<p>그리고 <code>age</code>와 <code>fare</code> 컬럼에 null이 들어간 데이터는 제외하고 <code>Train/Test</code> 셋으로 구분해주었다.</p>
<pre><code class="language-python"># null 데이터 제외
titanic = titanic[titanic[&#39;age&#39;].notnull()]
titanic = titanic[titanic[&#39;fare&#39;].notnull()]


# 데이터 분리
from sklearn.model_selection import train_test_split

X = titanic[[&#39;pclass&#39;, &#39;age&#39;, &#39;sibsp&#39;, &#39;parch&#39;, &#39;fare&#39;, &#39;gender&#39;, &#39;grade&#39;]].astype(&#39;float&#39;)
y = titanic[&#39;survived&#39;]

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size= 0.2, random_state=13)</code></pre>
<p>이제 주성분 분석 후 KNN을 적용시켜보자.</p>
<pre><code class="language-python"># 7개의 컬럼을 2개의 주성분으로 변환
pca_data, pca = get_pca_data(X_train, n_components= 2)
print_variance_ratio(pca)</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>variance_ratio :  [0.93576422 0.0632686 ]
sum of variance_ratio :  0.9990328289217747</p>
<p>2개의 주성분으로 분석했을 때 설명력이 99%이다...(??) 한 번 시각화해보자.</p>
<pre><code class="language-python">pca_pd = get_pd_from_pca(pca_data, pca.components_.shape[0])
pca_pd[&#39;survived&#39;] = y_train

sns.pairplot(pca_pd, hue = &#39;survived&#39;, height = 5, x_vars = [&#39;pca_0&#39;], y_vars = [&#39;pca_1&#39;])</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/81f977e9-3ac4-4733-b439-0bfa6bf52390/image.png" alt=""></p>
<p>위 시각화만 보았을 땐 생존 여부의 구분이 쉬울 것 같아보이진 않는다. 3개의 주성분으로 변환시켜보자.</p>
<pre><code class="language-python">pca_data, pca = get_pca_data(X_train, n_components= 3)

pca_pd = get_pd_from_pca(pca_data, pca.components_.shape[0])
pca_pd[&#39;survived&#39;] = y_train

import plotly.express as px

fig = px.scatter_3d(pca_pd, x = &#39;pca_0&#39;, y = &#39;pca_1&#39;, z = &#39;pca_2&#39;,
                    color = &#39;survived&#39;, symbol = &#39;survived&#39;,
                    opacity= 0.4)
fig.update_layout(margin = dict(l = 0, r = 0, b = 0, t  =0))
fig.show()</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/2051537e-6aa0-420c-816b-1de30e0aa9de/image.png" alt=""></p>
<p>3차원의 그림은 이렇게 <code>plotly</code>를 이용하면 더 입체적으로 그릴 수 있다.</p>
<pre><code class="language-python"># pipeline 으로 survivied 예측
estimators = [
    (&#39;scaler&#39; , StandardScaler()),
    (&#39;pca&#39;, PCA(n_components=3)),
    (&#39;clf&#39;, KNeighborsClassifier(n_neighbors=20))
]

pipe = Pipeline(estimators)
pipe.fit(X_train, y_train)

pred = pipe.predict(X_test)
accuracy_score(y_test, pred)</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>0.7655502392344498</p>
<p>그리고 KNN으로 예측한 정확도는 76%이다. </p>
<p>디카프리오라는 인물의 데이터를 예상해서 위 모델에 적용시킨 생존율은 아래와 같다.</p>
<pre><code class="language-python"># [&#39;pclass&#39;, &#39;age&#39;, &#39;sibsp&#39;, &#39;parch&#39;, &#39;fare&#39;, &#39;gender&#39;, &#39;grade&#39;]
# grade : [&#39;Miss&#39;, &#39;Mr&#39;, &#39;Mrs&#39;, &#39;Rare_f&#39;, &#39;Rare_m&#39;]

dicaprio = np.array([[3,18,0,0,5,1,1]])
winslet = np.array([[1,16,1,1,100,0,3]])

print(&quot;Decaprio : &quot;, pipe.predict_proba(dicaprio)[0,1])
print(&quot;Winslet : &quot;, pipe.predict_proba(winslet)[0,1])</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>Decaprio :  0.1
Winslet :  0.85</p>
<p><br><br></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[머신러닝] NLP(워드클라우드 색상 변경, 법안 실습 / 나이브베이즈 감성분석/ 문장 유사도)]]></title>
            <link>https://velog.io/@castle_mi/%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-NLP%EC%9B%8C%EB%93%9C%ED%81%B4%EB%9D%BC%EC%9A%B0%EB%93%9C-%EC%83%89%EC%83%81-%EB%B3%80%EA%B2%BD-%EB%B2%95%EC%95%88-%EC%8B%A4%EC%8A%B5-%EB%82%98%EC%9D%B4%EB%B8%8C%EB%B2%A0%EC%9D%B4%EC%A6%88-%EA%B0%90%EC%84%B1%EB%B6%84%EC%84%9D-%EB%AC%B8%EC%9E%A5-%EC%9C%A0%EC%82%AC%EB%8F%84</link>
            <guid>https://velog.io/@castle_mi/%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-NLP%EC%9B%8C%EB%93%9C%ED%81%B4%EB%9D%BC%EC%9A%B0%EB%93%9C-%EC%83%89%EC%83%81-%EB%B3%80%EA%B2%BD-%EB%B2%95%EC%95%88-%EC%8B%A4%EC%8A%B5-%EB%82%98%EC%9D%B4%EB%B8%8C%EB%B2%A0%EC%9D%B4%EC%A6%88-%EA%B0%90%EC%84%B1%EB%B6%84%EC%84%9D-%EB%AC%B8%EC%9E%A5-%EC%9C%A0%EC%82%AC%EB%8F%84</guid>
            <pubDate>Mon, 02 Oct 2023 08:54:10 GMT</pubDate>
            <description><![CDATA[<p>✍🏻 2일 공부 이야기.</p>
<p><a href="https://github.com/castlemi99/ZeroBaseDataSchool/blob/main/Machine%20Learning/11.%20NLP.ipynb"><img src="https://velog.velcdn.com/images/castle_mi/post/6e6fb17b-7e77-42ad-ba84-6a52226a70d7/image.png" alt=""></a>
오늘 공부한 실습 코드는 위 깃허브 사진을 클릭하면 이동합니다 :)</p>
<br>


<h2 id="star-wars">Star Wars</h2>
<p>이번에는 스타워즈 글을 가지고 워드클라우드를 해보자. </p>
<p><a href="https://github.com/PinkWink/ML_tutorial/tree/master/nltk_dataset"><img src="https://velog.velcdn.com/images/castle_mi/post/b8be2572-243a-4b47-9931-b2ad0c724ba2/image.png" alt=""></a></p>
<p>데이터는 위에서 다운받아주었다.</p>
<pre><code class="language-python">text = open(&#39;./data/06_a_new_hope.txt&#39;).read()

text = text.replace(&quot;HAN&quot;, &quot;Han&quot;)
text = text.replace(&quot;LUKE&#39;S&quot;, &quot;LUKE&quot;)

mask = np.array(Image.open(&#39;./data/06_stormtrooper_mask.png&#39;))</code></pre>
<p>먼저 데이터를 다운받아주고 수정하고자 하는 단어는 수정해준다.</p>
<pre><code class="language-python"># STOPWORDS : 큰 의미가 없는 단어 토큰 모음
stopwords = set(STOPWORDS)
stopwords.add(&#39;int&#39;)
stopwords.add(&#39;ext&#39;)</code></pre>
<p>큰 의미가 없는 단어들도 추가로 설정해주고</p>
<pre><code class="language-python">wc = WordCloud(
    max_words = 1000, mask = mask, stopwords = stopwords,
    margin = 10
).generate(text)

default_colors = wc.to_array()</code></pre>
<p><code>WordCloud</code>를 설정해준다. 이때 기본으로 설정되어있는 색상값들을 알아볼 수도 있다.</p>
<br>

<p>하지만 이번에는 마스크 안에 들어가는 색상값을 좀 바꿔보자. 공식문서를 보면 다음과 같은 커스텀 색상이 가능한 것으로 나와있다.</p>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/b08ed940-1c08-42f8-9d4e-79d06e491713/image.png" alt=""></th>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/1bcf11ce-ed23-4a39-ad20-c3adfc9fe463/image.png" alt=""></th>
</tr>
</thead>
</table>
<p><a href="https://amueller.github.io/word_cloud/auto_examples/a_new_hope.html">https://amueller.github.io/word_cloud/auto_examples/a_new_hope.html</a></p>
<p>위 링크로 들어가면 코드도 나와있다. 자세한 설명은 나와있지 않아서 좀 더 조사해보았다.</p>
<ul>
<li>word: 현재 처리 중인 단어</li>
<li>font_size: 해당 단어에 사용될 폰트 크기</li>
<li>position: 해당 단어의 위치</li>
<li>orientation: 해당 단어의 방향</li>
<li>random_state: 무작위로 생성된 상태 값으로써 색상 선택에 사용될 수 있음 (선택적 매개변수)</li>
<li>**kwargs: 기타 추가적인 매개변수들이 포함될 수 있음</li>
</ul>
<p><code>return &#39;hsl(0, 0%%, %d%%)&#39; % random.randint(60, 100)</code>과 같은 형식으로 색상값을 반환하는데를 HSL(Hue-Saturation-Lightness) 색상 모델을 사용하는 것이었다. </p>
<p>또한 아래와 같은 추가적인 내용이 있었다.</p>
<blockquote>
<p>첫 번째 파라미터인 &#39;0&#39;은 Hue 값을 나타내며 범위는 0부터 360까지 가능합니다. 두 번째 파라미터인 &#39;0%%&#39;은 Saturation 값을 나타내며 퍼센트로 표현되고 보통 그대로 &#39;0%&#39;로 설정하여 채도 없음을 의미합니다. 세 번째 파라미터 %d%%&#39;은 Lightness 값을 나타내며 퍼센트로 표현되고 여기서는 무작위로 생성된 정수값이 들어갑니다.</p>
</blockquote>
<p>따라서 위 코드에서는 &#39;Hue=0&#39;, &#39;Saturation=0%&#39;, &#39;Lightness=60%~100%&#39; 범위 내에서 임의의 회색 계열 색상을 반환하게 됩니다.</p>
<p>하지만 이렇게 커스텀한 색깔을 이용하는 것 보단<code>matplotlib</code>의 colormap을 이용하면 훨씬 편리할 것 같았다! </p>
<p>📌 워드클라우드 글자 컬러맵 설정 : <a href="https://jimmy-ai.tistory.com/135">https://jimmy-ai.tistory.com/135</a></p>
<br>

<pre><code class="language-python"># 마스크 안의 글자를 회색 톤으로 맞추기 위한 방법
import random

def grey_color_func(word, font_size, position, orientation, random_state = None, **kwargs):
    return &#39;hsl(0,0%%, %d%%)&#39; % random.randint(60,100)

import matplotlib.pyplot as plt

plt.figure(figsize = (12,12))
plt.imshow(wc.recolor(color_func=grey_color_func, random_state=13))
plt.axis(&#39;off&#39;)
plt.show()</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/a370b3cc-ae23-49aa-9248-53a19e5dad09/image.png" alt=""></p>
<p>워드클라우드에 적절한 색상값을 표현할 수도 있는 것을 배웠다.</p>
<p>다음은 법안 내용을 실습해보자.</p>
<br>

<h2 id="육아휴직관련-법안">육아휴직관련 법안</h2>
<p><code>KoNLPy</code>는 대한민국 법령을 가지고 있어서 편리하게 파이썬으로 읽어들일 수 있다. 지금은 많은 법령 중 <code>제 1809890호</code> 의안을 이용해보자.</p>
<pre><code class="language-python">import matplotlib.pyplot as plt
%matplotlib inline
from matplotlib import rc
rc(&quot;font&quot;, family = &quot;Malgun Gothic&quot;)</code></pre>
<p>한글을 시각화하기 때문에 한글 설정이 필요했다.</p>
<pre><code class="language-python">import nltk
from konlpy.corpus import kobill

doc_ko = kobill.open(&#39;1809890.txt&#39;).read()
doc_ko</code></pre>
<p>모든 워드클라우드의 시작은 데이터 읽어들이기.</p>
<p>해당 라이브러리들을 import해주고 <code>kobill.open(&#39;법령 번호.txt&#39;).read()</code>를 통해 쉽게 법령을 텍스트로 읽을 수 있다.</p>
<br>

<p>그리고 앞서 배운 명사를 분석해주는 함수는 아래와 같다.</p>
<pre><code class="language-python">from konlpy.tag import Okt

t = Okt()
tokens_co = t.nouns(doc_ko)
tokens_co</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/ee9da0d8-7562-4e65-91c8-106186ad3638/image.png" alt=""></p>
<p>그리고 명사 분석된 토큰들을 별명을 붙여 다양한 기능을 사용할 수 있게 만들어주기 위해 </p>
<pre><code class="language-python">ko = nltk.Text(tokens_co, name = &#39;육아휴직법&#39;)</code></pre>
<p>코드를 실행시켜주었다. 위는 <code>tokens_co</code> 토큰들을 육아휴직법이란 별명을 붙여준 코드이다.</p>
<p>이렇게 <code>ko</code> 변수는 다양한 작업을 할 수 있다.</p>
<ul>
<li><code>len(ko)</code> : 전체 명사 개수 출력</li>
<li><code>ko.tokens</code> : 전체 명사 출력</li>
<li><code>len(set(ko.tokens))</code> : 중복되지 않은 유일한 명사 단어 출력</li>
<li><code>ko.vocab()</code> : 각 명사들의 호출 횟수 출력</li>
<li><code>ko.count(&#39;원하는 단어&#39;)</code> : 특정 단어의 빈도수 출력</li>
<li><code>ko.concordance(&#39;원하는 단어&#39;)</code> : 해당 단어 좌우에 있는 단어를 같이 출력</li>
<li><code>ko.collocations()</code> : 연관된 단어(collocation)를 출력해주는데 이는 null값으로 반환될 수도 있음</li>
</ul>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/ae6224e0-1c3e-4f96-add7-5a5c07f70ea4/image.png" alt=""></p>
<p>위와 같이 가장 많이 사용된 명사들을 시각화할 수도 있다. <code>ko.plot(원하는 개수)</code> 를 입력해주면 가장 많이 호출된 명사 개수 만큼 그 호출 횟수와 함께 시각화해준다.</p>
<p>하지만 여기서 만, 액, 세 등 불필요한 단어도 섞여있는 것을 볼 수 있다. 이들을 제거해주기 위해선 stopwords에 넣어주어야하는데 한글은 그것이 좀 복잡해서 일일이 손으로 집어넣어주어야하는 경우가 대부분이다.</p>
<pre><code class="language-python">stop_words = [
    &#39;.&#39;, &#39;(&#39;, &#39;)&#39;, &#39;,&#39;, &quot;&#39;&quot;, &#39;%&#39;, &quot;-&quot;, &quot;x&quot;, &quot;).&quot;, 
    &quot;의&quot;, &quot;자&quot;, &quot;에&quot;, &quot;안&quot;, &quot;번&quot;, &quot;호&quot;, &quot;을&quot;, &quot;이&quot;, &quot;다&quot;, &quot;만&quot;,
    &quot;로&quot;, &quot;가&quot;, &quot;를&quot;
]

# 위 stop_words를 제외한 명사를 다시 설정해줌
ko = [each_word for each_word in ko if each_word not in stop_words]</code></pre>
<p>불필요한 단어들을 제외한 명사 분석된 토큰들을 다시 불러주고 시각화를 다시 해보면 아래와 같다.</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/24486b5c-83ed-4ca1-9e3f-8be5fcb1bf76/image.png" alt=""></p>
<pre><code class="language-python">plt.figure(figsize=(16,6))
ko.dispersion_plot([&#39;육아휴직&#39;, &#39;초등학교&#39;, &#39;공무원&#39;])</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/65db8832-a36a-4510-8efe-811a07676540/image.png" alt=""></p>
<p>또한 위와 같이 <code>ko.dispersion_plot([&#39;원하는 단어&#39;])</code>를 이용하면 전체 단어들 중 해당 단어가 어디에 위치해있는지를 알 수 있다.</p>
<p>그렇다면 이제 마지막으로 워드클라우드를 해보면 아래와 같은 결과를 볼 수 있다. </p>
<pre><code class="language-python">data = ko.vocab().most_common(150) #가장 많이 불린 단어 150개

wordcloud = WordCloud(
    font_path = &quot;C:\Windows\Fonts\malgun.ttf&quot;, # 글씨체
    relative_scaling = 0.2, # 단어 사이 간격
    background_color = &#39;white&#39; # 배경색
).generate_from_frequencies(dict(data))

plt.figure(figsize=(12, 8))
plt.imshow(wordcloud)
plt.axis(&#39;off&#39;)
plt.show()</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/4bf2b0af-a264-4ea5-a0ad-8f97f3e50cf6/image.png" alt=""></p>
<p><br><br><br></p>
<h2 id="나이브베이즈-감성분석">나이브베이즈 감성분석</h2>
<pre><code class="language-python">from nltk.tokenize import word_tokenize
import nltk</code></pre>
<p>나이브 베이즈 분류기는 자연어 처리에서 가장 많이 사용되는 분류기이다. 기계학습 분야에서 특성들 사이의 독립을 가정하는 베이즈 정리를 적용한 확률 분류기로 광범위하게 연구되고 있다.</p>
<p>영어 버전과 한글 버전 두 가지 버전의 감성 분석을 짧게 실습해보려고 한다. </p>
<p>언어와 관계없이 나이브베이즈 감성 분석의 단계는 아래와 같다.</p>
<blockquote>
<ol>
<li>Train 데이터 만들기(지도학습이기 때문에 정답지도 같이!)</li>
<li>Train 데이터 문장들을 단어 단위로 쪼개어 중복되지 않은 유일한 단어들만 모인 전체 말뭉치 만들기</li>
<li>전체 말뭉치 속 단어들이 Train 데이터의 각 문장 속 단어에 포함되어있는지 여부를 정답지 라벨과 같이 출력하기</li>
<li>나이브베이즈 분류기 학습시키기</li>
<li>테스트 데이터를 만들고 단어를 쪼개 전체 말뭉치 속 단어 대비 테스트 문장 단어 포함 여부 출력하기</li>
<li>학습된 나이브베이즈 분류기에 넣어 예측시키기</li>
</ol>
</blockquote>
<p>전체 방향은 위와 같지만 한글은 형태소에 따라 분석 결과가 많이 달라져서 좀 더 추가되는 내용이 있다. 아래 실습 코드를 보고 다시 또 살펴보자.</p>
<br>

<h3 id="영어">영어</h3>
<p><strong>📌 1. Train 데이터 만들기</strong></p>
<pre><code class="language-python">train = [
    (&quot;i like you&quot;, &quot;pos&quot;),
    (&quot;i hate you&quot;, &quot;neg&quot;),
    (&quot;you like me&quot;, &quot;neg&quot;),
    (&quot;i like her&quot;, &quot;pos&quot;)
]</code></pre>
<p><strong>📌 2. 전체 말뭉치 만들기</strong></p>
<pre><code class="language-python">sentence = train[0] # (&quot;i like you&quot;, &quot;pos&quot;)
word_tokenize(sentence[0]) # word_tokenize 는 띄어쓰기를 기준으로 단어를 분리해줌</code></pre>
<p>이때 <code>sentence</code>는 <code>(&quot;i like you&quot;, &quot;pos&quot;)</code>를 반환하고 <code>word_tokenize(sentence[0])</code>의 <code>word_tokenize</code>는 띄어쓰기를 기준으로 단어를 분리해주기 때문에 위의 결과는 <code>[&#39;i&#39;, &#39;like&#39;, &#39;you&#39;]</code>가 된다. 이를 이용해서 중복되지 않은 Train 데이터의 단어들을 추출할 수 있다.</p>
<pre><code class="language-python">all_words = set(
    word.lower() for sentence in train for word in word_tokenize(sentence[0])
)
all_words </code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>{&#39;hate&#39;, &#39;her&#39;, &#39;i&#39;, &#39;like&#39;, &#39;me&#39;, &#39;you&#39;}</p>
<p>set을 이용해 중복되지 않은 유일한 단어들만 추출했다.</p>
<br>

<p><strong>📌 3. <code>{전체 말뭉치 단어 : Train 문장 포함 여부}, Train 라벨</code> 형태로 출력하기</strong></p>
<pre><code class="language-python">#  전체 말뭉치 속 각 단어 : train 문장 단어에 속하는지 여부(True/False), train 라벨 
t = [({word : (word in word_tokenize(x[0])) for word in all_words} , x[1]) for x in train]
t</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/92fe355f-c3d6-4327-824a-08a5467ba481/image.png" alt=""></p>
<p><strong>📌 4. 나이브베이즈 분류기 학습시키기</strong></p>
<pre><code class="language-python">classifier = nltk.NaiveBayesClassifier.train(t) # 훈련
classifier.show_most_informative_features()  </code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/943d1cb0-2e37-4219-9622-5f33b8448403/image.png" alt=""></p>
<p>nltk에서는 sklearn의 fit이 train이다. <code>train</code>을 실행시켜 분류기를 학습시켜준 후 <code>show_most_informative_features()</code>을 통해 가장 많은 정보를 담고 있는 특성을 볼 수도 있다.</p>
<p>출력된 결과를 해석해보자면, 아래와 같이 해석할 수 있다.</p>
<p>&#39;hate&#39; 라는 단어가 없을 때 pos:neg의 비율은 1.7:1이다. 그리고 &#39;her&#39;라는 단어가 없을 때 neg:pos 의 비율은 1.7:1이다.</p>
<br>

<p><strong>📌 5. 테스트 데이터 만들기 및 분류기에 들어갈 상태 만들어주기</strong></p>
<pre><code class="language-python">test_sentence = &#39;i like MeRui&#39;

test_sent_features = {
    word.lower() : (word in word_tokenize(test_sentence.lower())) for word in all_words
}
test_sent_features # 전체 말뭉치 대비 테스트 문장 단어 포함 여부</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/344a1f70-cb9f-4921-8687-cf7c6710501b/image.png" alt=""></p>
<p><strong>📌 6. 테스트 데이터 예측하기</strong></p>
<pre><code class="language-python">classifier.classify(test_sent_features)</code></pre>
<p><code>classify</code>는 sklearn의 predict와 같은 기능을 한다. 따라서 위 코드를 실행시켜주면 <code>&#39;pos&#39;</code>라는 테스트 데이터에 대한 예측값을 얻을 수 있다.</p>
<br>

<h3 id="한글">한글</h3>
<p>이번에는 한글로도 해보자.</p>
<p><strong>📌 1. Train 데이터 만들기</strong></p>
<pre><code class="language-python">from konlpy.tag import Twitter

pos_tagger = Twitter()

train = [
    (&quot;메리가 좋아&quot;, &quot;pos&quot;),
    (&quot;고양이도 좋아&quot;, &quot;pos&quot;),
    (&#39;난 수업이 지루해&#39;, &#39;neg&#39;),
    (&#39;메리는 이쁜 고양이야&#39;, &#39;pos&#39;)
]</code></pre>
<p><strong>📌 2. 전체 말뭉치 만들기</strong></p>
<pre><code class="language-python">all_words = set(
    word.lower() for sentence in train for word in word_tokenize(sentence[0])
)
all_words </code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>{&#39;고양이도&#39;, &#39;고양이야&#39;, &#39;난&#39;, &#39;메리가&#39;, &#39;메리는&#39;, &#39;수업이&#39;, &#39;이쁜&#39;, &#39;좋아&#39;, &#39;지루해&#39;}</p>
<p>여기서 &#39;고양이도&#39;, &#39;고양이야&#39;를 다른 것으로 인식하고 &#39;메리가&#39;와 &#39;메리는&#39; 또한 다른 언어로 인식하고 있다. 이대로 분석하면 문제가 생기기 때문에 <strong>한글 감성 분석을 할 때엔 형태소 분석이 꼭 필수이다.</strong></p>
<pre><code class="language-python">def tokenize(doc):
    return [&#39;/&#39;.join(t) for t in pos_tagger.pos(doc, norm = True, stem = True)]

train_docs = [(tokenize(row[0]), row[1]) for row in train]
train_docs</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>[([&#39;메리/Noun&#39;, &#39;가/Josa&#39;, &#39;좋다/Adjective&#39;], &#39;pos&#39;),
 ([&#39;고양이/Noun&#39;, &#39;도/Josa&#39;, &#39;좋다/Adjective&#39;], &#39;pos&#39;),
 ([&#39;난/Noun&#39;, &#39;수업/Noun&#39;, &#39;이/Josa&#39;, &#39;지루하다/Adjective&#39;], &#39;neg&#39;),
 ([&#39;메리/Noun&#39;, &#39;는/Josa&#39;, &#39;이쁘다/Adjective&#39;, &#39;고양이/Noun&#39;, &#39;야/Josa&#39;], &#39;pos&#39;)]</p>
<p>형태소 분석을 한 후 품사를 단어 뒤에 붙여주는 <code>tokenize</code>함수를 만들어 각 Train 데이터가 <code>단어/품사</code> 형태로 출력되게 만들어주었다.</p>
<pre><code class="language-python">tokens = [t for d in train_docs for t in d[0]]
tokens</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/1ccbae1f-bc26-44fc-85f9-168232a29731/image.png" alt=""></p>
<p>그리고 각 <code>단어/품사</code> 를 출력하여 전체 말뭉치를 만들어주었다.</p>
<br>

<p><strong>📌 3. <code>{전체 말뭉치 단어 : Train 문장 포함 여부}, Train 라벨</code> 형태로 출력하기</strong></p>
<pre><code class="language-python">def term_exists(doc):
    return {word : (word in set(doc)) for word in tokens}

train_xy = [(term_exists(d), c) for d, c in train_docs]
train_xy</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/886d86d4-cd8b-4e89-bab7-a31fbf7bfa96/image.png" alt=""></p>
<p><strong>📌 4. 나이브베이즈 분류기 학습시키기</strong></p>
<pre><code class="language-python">classifier = nltk.NaiveBayesClassifier.train(train_xy)</code></pre>
<br>

<p><strong>📌 5. 테스트 데이터 만들기 및 분류기에 들어갈 상태 만들어주기</strong></p>
<pre><code class="language-python">test_sentence = [(&#39;난 마치면 메리랑 놀거야&#39;)]
test_docs = pos_tagger.pos(test_sentence[0])
test_sent_features = {word : (word in tokens) for word in test_docs}
test_sent_features</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/e2a6f93a-04b3-4390-adef-517171d47741/image.png" alt=""></p>
<p><strong>📌 6. 테스트 데이터 예측하기</strong></p>
<pre><code class="language-python">classifier.classify(test_sent_features)</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>&#39;pos&#39;</p>
<p>한글 감성 분석에서 2번의 형태소 분석을 하지 않았을 때 해당 테스트 문장의 예측값은 &#39;neg&#39;가 나왔다. 이렇듯 한글에서는 형태소 분석이 필수임을 잊지 말자!!</p>
<p><br><br><br></p>
<h2 id="문장-유사도">문장 유사도</h2>
<p>문장을 벡터로 표현할 수 있으면 두 점 사이 거리를 구하는 공식으로 두 문장 간 유사도를 측정할 수도 있을 것 같다.</p>
<p>문장 유사도를 측정할 수 있는 방법 2가지를 소개하겠다.
거리를 측정하는 것은 지도 학습이 아니기 때문에 따로 정답지는 필요없다. 문장 유사도를 측정하는 방법은 아래와 같은 전체적인 과정을 실행시킨다.</p>
<blockquote>
<ol>
<li>Vectorizer 호출</li>
<li>Train 데이터 생성</li>
<li>(한글의 경우) 형태소 분석을 통해 Train 데이터를 변환</li>
<li>Vectorizer fit_transform</li>
<li>Test 데이터 생성 및 분류기에 들어갈 형태 만들어주기</li>
<li>거리 측정 후 유사한 문장 반환</li>
</ol>
</blockquote>
<h3 id="count-vectorize">Count Vectorize</h3>
<p><strong>📌 1. Vectorizer 호출</strong></p>
<pre><code class="language-python">from sklearn.feature_extraction.text import CountVectorizer

vectorizer = CountVectorizer(min_df = 1)</code></pre>
<br>

<p><strong>📌 2. Train 데이터 생성</strong></p>
<pre><code class="language-python"># 거리만 측정하는 것이므로 지도학습이 아니다.

contents = [
    &#39;상처받은 아이들은 너무 일찍 커버려&#39;,
    &#39;내가 상처받은 거 아는 사람 불편해&#39;,
    &#39;잘 사는 사람들은 좋은 사람 되기 쉬워&#39;,
    &#39;아무 일도 아니야 괜찮아&#39;
]</code></pre>
<br>

<p><strong>📌 3. (한글의 경우) 형태소 분석을 통해 Train 데이터를 변환</strong></p>
<pre><code class="language-python">from konlpy.tag import Okt

t = Okt() # 형태소 분석 엔진
contents_tokens = [t.morphs(row) for row in contents]</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/87420ac6-9469-493b-b10b-a3dc67b1dee3/image.png" alt=""></p>
<p><code>morphs</code>를 사용하면 위와 같이 형태소들이 추출되는 것을 볼 수 있다. 우리는 이후 <code>nltk</code>의 <code>CountVectorizer</code>를 사용할 것인데 이는 띄어쓰기로 단어를 구분한다. 따라서 그에 맞는 형태를 만들어주기 위해 형태소 단위로 잘라진 문장을 띄어쓰기하여 다시 하나의 문장으로 합쳐주는 작업이 필요하다.</p>
<pre><code class="language-python">contents_for_vectorize = []

for content in contents_tokens:
    sentence = &#39;&#39;
    for word in content:
        sentence = sentence + &#39; &#39; + word
    contents_for_vectorize.append(sentence)

contents_for_vectorize</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/3a67b7b3-87d0-4c6a-8324-030e51439b02/image.png" alt=""></p>
<br>

<p><strong>📌 4. Vectorizer fit_transform</strong></p>
<pre><code class="language-python">X = vectorizer.fit_transform(contents_for_vectorize)
X</code></pre>
<p>이때 아래와 같은 해석을 할 수 있다. </p>
<ul>
<li><p>Train 문장 개수, 전체 말뭉치 단어 개수 </p>
<pre><code class="language-python">num_samples, num_features = X.shape 
# (4, 17)
# 문장 개수 , 전체 말뭉치 단어 개수 출력</code></pre>
</li>
<li><p>전체 말뭉치 단어</p>
<pre><code class="language-python">vectorizer.get_feature_names_out() # 전체 말뭉치 단어</code></pre>
</li>
<li><p>전체 말뭉치 대비 Train 데이터 단어 호출 개수 반환</p>
<pre><code class="language-python">X.toarray().transpose()</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/2d35c5d0-3dd9-40c6-aa51-127c8759ffbb/image.png" alt=""></p>
</li>
</ul>
<p>행은 전체 말뭉치 단어, 열은 각 Train 문장에 해당한다. 
위 행렬을 통해 전체 말뭉치 단어 대비 Train 데이터 단어가 몇 번 호출되었는지 알 수 있다.</p>
<br>

<p>*<em>📌 5. Test 데이터 생성 및 분류기에 들어갈 형태 만들어주기
*</em></p>
<pre><code class="language-python">new_post = [&#39;상처받기 싫어 괜찮아&#39;]
new_post_tokens = [t.morphs(row) for row in new_post]

new_post_for_vectorize = []

for content in new_post_tokens:
    sentence = &#39;&#39;
    for word in content:
        sentence = sentence + &quot; &quot; + word
    new_post_for_vectorize.append(sentence)
new_post_for_vectorize</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>[&#39; 상처 받기 싫어 괜찮아&#39;]</p>
<pre><code class="language-python"># 테스트용 문장을 벡터화

new_post_vec = vectorizer.transform(new_post_for_vectorize)
new_post_vec.toarray()</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>array([[1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=int64)</p>
<br>


<p>** 📌 6. 거리 측정 후 유사한 문장 반환**</p>
<pre><code class="language-python">import scipy as sp 

def dist_raw(v1, v2):
    delta = v1 - v2 
    return sp.linalg.norm(delta.toarray())</code></pre>
<p>단순한 두 사이 거리를 구하는 공식을 이용한 함수를 하나 만들어주고 Train 데이터와 Test 데이터 사이의 거리를 구해준다.</p>
<pre><code class="language-python">dist = [dist_raw(each, new_post_vec) for each in X]</code></pre>
<p><code>dist</code>에는 <code>[2.449489742783178, 2.23606797749979, 3.1622776601683795, 2.0]</code>와 같은 결과값이 담겨있고 </p>
<pre><code class="language-python">print(&quot;Best post index is &quot;, dist.index(min(dist)), &#39;dist = &#39;, min(dist))
print(&quot;Test post is --&gt; &quot;, new_post)
print(&quot;Best dist post is --&gt; &quot;, contents[dist.index(min(dist))])</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>Best post index is  3 dist =  2.0
Test post is --&gt;  [&#39;상처받기 싫어 괜찮아&#39;]
Best dist post is --&gt;  아무 일도 아니야 괜찮아</p>
<p>거리가 가장 작은 문장을 추출하여 가장 유사한 문장을 추출할 수 있다.</p>
<p>결국은 문장을 벡터로 잘 만드는 것과 만들어진 벡터 사이의 거리를 계산하는 것이 중요하다.</p>
<p>그렇다면 벡터화하는 방법 중에 단어 호출 횟수를 세는 것 이외에 다른 방법은 무엇이 있을까?</p>
<h3 id="tf-idf-vectorize">TF-IDF Vectorize</h3>
<p><a href="https://ko.wikipedia.org/wiki/Tf-idf"><img src="https://velog.velcdn.com/images/castle_mi/post/e2a028d8-2cc3-4b8f-b4fd-a94ddbf5f3ce/image.png" alt=""></a></p>
<pre><code class="language-python">from sklearn.feature_extraction.text import TfidfVectorizer

vectorizer = TfidfVectorizer(min_df=1, decode_error=&#39;ignore&#39;)</code></pre>
<p><code>tf-idf</code>를 이용해서도 두 점 사이의 거리를 구할 수 있다. </p>
<p>앞서 <code>CountVectorizer</code>를 이용한 코드를 모두 <code>TfidfVectorizer</code>로 바꿔주고 내용은 그대로이다.</p>
<p>또한 두 점 사이의 거리를 구할 때 정규화를 진행시킨 후 구할 수도 있다. 정규화를 한 후 두 점 사이의 거리를 구하게 되면 한 쪽의 특성이 두드러지는 것을 방지할 수 있다는 장점이 있다.</p>
<pre><code class="language-python">def dist_norm(v1, v2):
    v1_normalized = v1 / sp.linalg.norm(v1.toarray())
    v2_normalized = v2 / sp.linalg.norm(v2.toarray())

    delta = v1_normalized - v2_normalized

    return sp.linalg.norm(delta.toarray())</code></pre>
<p>아래는 <code>TfidfVectorizer</code>을 이용한 전체 코드이다.</p>
<pre><code class="language-python">from sklearn.feature_extraction.text import TfidfVectorizer

vectorizer = TfidfVectorizer(min_df=1, decode_error=&#39;ignore&#39;)

# Train 데이터 벡터화
X = vectorizer.fit_transform(contents_for_vectorize)

# Test 데이터 벡터화
new_post_vec = vectorizer.transform(new_post_for_vectorize)


# Train - Test 사이 간 거리 
dist = [dist_norm(each, new_post_vec) for each in X]

# Test 문장과 유사한 Train 문장 추출
print(&quot;Best post index is &quot;, dist.index(min(dist)), &#39;dist = &#39;, min(dist))
print(&quot;Test post is --&gt; &quot;, new_post)
print(&quot;Best dist post is --&gt; &quot;, contents[dist.index(min(dist))])</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p>Best post index is  3 dist =  1.1021396119773588
Test post is --&gt;  [&#39;상처받기 싫어 괜찮아&#39;]
Best dist post is --&gt;  아무 일도 아니야 괜찮아</p>
<p><br><br><br></p>
<p>위 내용을 바탕으로 네이버 API를 호출하여 지식인 내용 중 유사한 문장을 추출한 실습도 진행해보았는데 코드가 비슷해서 깃허브에 올려둔 파일을 참고하면 좋을 것 같다.</p>
<p><a href="https://velog.io/@castle_mi/EDA-Chapter06.-%EC%8B%9C%EA%B3%84%EC%97%B4-%EB%B6%84%EC%84%9DProphet-Chapter07.-Naver-API">🖱️ 네이버 API 공부 정리 링크</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[머신러닝] credit card fraud detection / NLP(Natural Language Processing, 자연어 처리)]]></title>
            <link>https://velog.io/@castle_mi/%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-credit-card-fraud-detection-NLPNatural-Language-Processing-%EC%9E%90%EC%97%B0%EC%96%B4-%EC%B2%98%EB%A6%AC</link>
            <guid>https://velog.io/@castle_mi/%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-credit-card-fraud-detection-NLPNatural-Language-Processing-%EC%9E%90%EC%97%B0%EC%96%B4-%EC%B2%98%EB%A6%AC</guid>
            <pubDate>Wed, 27 Sep 2023 07:16:50 GMT</pubDate>
            <description><![CDATA[<p>✍🏻 27일 공부 이야기.</p>
<p>오늘 실습한 코드는 아래 깃허브 사진을 클릭하면 이동합니다.</p>
<p><a href="https://github.com/castlemi99/ZeroBaseDataSchool/tree/main/Machine%20Learning"><img src="https://velog.velcdn.com/images/castle_mi/post/89cbd094-b63b-4c0b-bd6a-dfe752965f59/image.png" alt=""></a></p>
<p><br><br></p>
<h1 id="credit-card-fraud-detection신용카드-부정-사용-검출">credit card fraud detection(신용카드 부정 사용 검출)</h1>
<p><a href="https://www.kaggle.com/datasets/mlg-ulb/creditcardfraud"><img src="https://velog.velcdn.com/images/castle_mi/post/46909a8e-d8da-4a69-9aa8-9d46512775da/image.png" alt=""></a></p>
<p>이 또한 캐글 데이터이다.</p>
<p>위 데이터는 신용카드 사기 검출 분류 실습용 데이터이며 <code>Class</code> 컬럼이 사기 유무를 의미한다.(우리의 타겟 데이터!!)</p>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/f7d713eb-b7a9-4918-b689-126a4b1e581b/image.png" alt=""></th>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/8103978a-6770-44a5-908f-1532fdb7070b/image.png" alt=""></th>
</tr>
</thead>
</table>
<p><code>Class</code> 컬럼은 전체 데이터의 약 0.17%만이 사기를 의미하는 <code>1</code>을 가지고 있어 데이터 불균형이 굉장히 심하다.</p>
<p>일단, <code>Class</code>의 분포를 Train셋과 Test셋에 동일하게 적용하여 데이터를 나누고 데이터의 불균형을 확인하면 아래와 같다.</p>
<pre><code class="language-python">X = raw_data.iloc[:, 1:-1] # V1 ~ Amount 컬럼까지
y = raw_data.iloc[:, -1] # Class 컬럼


X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2,
                                                    stratify=y,
                                                    random_state=13)


np.unique(y_train, return_counts = True)</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/ab680264-7797-4a39-b75a-73483adc02f7/image.png" alt=""></p>
<br>

<p>그리고 여러 모델들을 돌릴 것이기 때문에 분류기 성능을 반환하는 함수와 roc 커브를 출력해주는 함수를 만들어두자.</p>
<p><strong>📌 분류기의 성능 확인 함수</strong></p>
<pre><code class="language-python"># 분류기 성능을 return하는 함수

from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, roc_auc_score

def get_clf_eval(y_test, pred):
    acc = accuracy_score(y_test, pred)
    pre = precision_score(y_test, pred)
    recall = recall_score(y_test, pred)
    f1 = f1_score(y_test, pred)
    roc_auc = roc_auc_score(y_test, pred)

    return acc , pre, recall, f1, roc_auc


# confusion matrix 출력 함수

from sklearn.metrics import confusion_matrix

def print_clf_eval(y_test, pred):
    confusion = confusion_matrix(y_test, pred)
    acc, pre, recall, f1, auc = get_clf_eval(y_test, pred)

    print(&#39;***Confusion matrix***&#39;)
    print(confusion)
    print(&#39;***********************&#39;)

    print(&quot;Accuracy : {0:.4f} , Precision : {1:.4f}&quot;.format(acc, pre))
    print(&quot;Recall : {0:.4f} , F1 : {1:.4f}, AUC : {2:.4f}&quot;.format(recall, f1, auc))</code></pre>
<p><strong>📌 roc 커브 출력 함수</strong></p>
<pre><code class="language-python"># roc 커브
from sklearn.metrics import roc_curve

def draw_roc_curve(models, model_names, X_test, y_test):
    plt.figure(figsize=(10,10))

    for model in range(len(models)):
        pred = models[model].predict_proba(X_test)[:,1]
        fpr, tpr, threshold = roc_curve(y_test, pred)
        plt.plot(fpr, tpr, label = model_names[model])

    plt.plot([0,1], [0,1], &#39;k--&#39;, label = &#39;random quess&#39;)
    plt.title(&#39;ROC&#39;)
    plt.legend()
    plt.grid()
    plt.show()</code></pre>
<br>


<h2 id="첫번째-시도---무작정-돌려보자">첫번째 시도 - 무작정 돌려보자</h2>
<p>그리고 이제 일단 무작정 모델들을 돌려보자.</p>
<p>우리가 배운 <code>LogisticRegression</code>, <code>DecisionTreeClassifier</code>, <code>RandomForestClassifier</code>, <code>LGBMClassifier</code>의 성능을 살펴보자.</p>
<br>

<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/7adcd9cb-4f2b-4e4c-a730-ca0c5f0bc9d5/image.png" alt=""> LogisticRegression</th>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/8265464f-9cc7-4cca-bd79-9c3ac6ed00e3/image.png" alt=""> Decision Tree</th>
</tr>
</thead>
<tbody><tr>
<td><img src="https://velog.velcdn.com/images/castle_mi/post/c55b8e6c-9be7-4635-8443-6eb1a6334521/image.png" alt=""> Random Forest</td>
<td><img src="https://velog.velcdn.com/images/castle_mi/post/48a81d90-46ef-42a1-bb93-329b61b1956e/image.png" alt=""> LightGBM</td>
</tr>
</tbody></table>
<p>각각의 모델들을 한 눈에 살펴보기엔 좀 힘들다. 모델과 Test 데이터를 입력하면 성능을 출력해주고 각 모델들의 성능을 데이터프레임으로 정리해주는 함수를 만들어보자.</p>
<br>

<p>📌 다수의 모델들의 성능을 데이터프레임으로 반환하는 함수</p>
<pre><code class="language-python"># 모델과 데이터 입력시 성능을 출력하는 함수

def get_result(model, X_train, y_train, X_test, y_test):
    model.fit(X_train, y_train)
    pred = model.predict(X_test)

    return get_clf_eval(y_test, pred)


# 다수의 모델들의 성능을 정리하여 데이터프레임으로 반환하는 함수

def get_result_pd(models, model_names, X_train, y_train, X_test, y_test):
    col_names = [&#39;accuracy&#39;, &#39;precision&#39;, &#39;recall&#39;, &#39;f1&#39;, &#39;roc_auc&#39;]
    tmp = []

    for model in models:
        tmp.append(get_result(model, X_train , y_train, X_test, y_test))

    return pd.DataFrame(tmp, columns=col_names, index = model_names)</code></pre>
<p>위 함수를 이용해서 앞서 네 가지 모델을 돌리면 아래와 같다.</p>
<pre><code class="language-python">models = [lr_clf, dt_clf, rf_clf, lgbm_clf]
model_names = [&#39;LogisticReg&#39;, &#39;DecisionTree&#39;, &#39;RandomForest&#39;, &#39;LightGBM&#39;]

results = get_result_pd(models, model_names, X_train, y_train, X_test, y_test)
results</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/a74446c7-24b9-41cd-9d7f-f44cc43e49db/image.png" alt=""></p>
<p>이때 우리는 무엇을 보아야할까? <code>accuracy</code>가 99%로 높다고 마냥 좋아하면 될까? </p>
<p>아니다.</p>
<p>우리가 가진 데이터는 현재 데이터의 불균형이 있기 때문에 잘 살펴보면 실제 <code>1(Fraud)</code>를 잘 예측할 수 있어야할 것이다. 그러면 우리에게 중요한 모델 성능 지표는? <code>Recall</code>일 것이다.</p>
<p><code>Recall</code>을 좀 더 향상시키기 위해 다른 시도도 해보자.</p>
<p><br><br></p>
<h2 id="두번째-시도---scaler">두번째 시도 - scaler</h2>
<pre><code class="language-python"># &#39;amount&#39;(신용카드 사용 금액) 컬럼 확인

plt.figure(figsize=(10,5))
sns.distplot(raw_data[&#39;Amount&#39;], color = &#39;r&#39;)
plt.show()</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/3a3fd6de-dd77-40ae-8553-0b1f7ffd4b69/image.png" alt=""></p>
<p>각 컬럼들의 데이터 분포를 살펴보다가 <code>Amount</code> 컬럼의 분포가 눈에 띄었다. 특정 범위에 너무 몰려있는데 만약 <code>1(Fraud)</code>을 구분하는데에 해당 컬럼이 영향을 끼친다면 데이터 분포가 좀 더 고르게 되어있어야 성능이 좋아질 것이다.</p>
<br>

<p><strong>먼저 <code>StandardScaler</code>를 적용해보자.</strong></p>
<pre><code class="language-python">from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
amount_n = scaler.fit_transform(raw_data[&#39;Amount&#39;].values.reshape(-1,1))

raw_data_copy = raw_data.iloc[:, 1:-2]
raw_data_copy[&#39;Amount_Scaled&#39;] = amount_n

X_train, X_test, y_train, y_test = train_test_split(raw_data_copy, y, test_size=0.2,
                                                    stratify=y,
                                                    random_state=13)
models = [lr_clf, dt_clf, rf_clf, lgbm_clf]
model_names = [&#39;LogisticReg&#39;, &#39;DecisionTree&#39;, &#39;RandomForest&#39;, &#39;LightGBM&#39;]

results = get_result_pd(models, model_names, X_train, y_train, X_test, y_test)
results   </code></pre>
<blockquote>
<p> 💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/52a91900-ea50-463b-8ab9-e7913f2d4ad0/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/697122c7-8942-45f0-84db-a0bd44b5b77b/image.png" alt=""></p>
<p>앞서 만든 함수를 통해 살펴본 roc 커브도 같이 보았는데 큰 성능 향상은 없어보인다.</p>
<br>

<p><strong>두번째로, <code>StandardScaler</code>대신 <code>log</code>를 취해보자.</strong></p>
<pre><code class="language-python">amount_log = np.log1p(raw_data[&#39;Amount&#39;])

raw_data_copy[&#39;Amount_Scaled&#39;] = amount_log


plt.figure(figsize=(10,5))
sns.distplot(raw_data_copy[&#39;Amount_Scaled&#39;], color = &#39;r&#39;)
plt.show()</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/bd1618d0-b4cc-4c0b-b647-52f0510ed423/image.png" alt=""></p>
<p>오! 얼추 데이터의 분포가 고르게 된 것 같다. 성능이 좀 괜찮아졌을까!??</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/d4f44d9b-d699-46af-ab38-01d295c9f107/image.png" alt=""></p>
<p>음.. 별 차이는 없는 것 같다.</p>
<p>다른 처리가 필요할 듯 싶은데 무엇을 더 할 수 있을까?</p>
<p><br><br></p>
<h2 id="세번째-시도---이상치-제거">세번째 시도 - 이상치 제거</h2>
<p>이상치들을 제거해보자.</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/636ea366-6549-4631-94c0-e82586cbd1c8/image.png" alt=""></p>
<p>위 세 컬럼들을 보았을 때 박스 플럿의 꼬리에서 벗어난 이상치들이 많은 것 같다.</p>
<p><code>1(Fraud)</code>들이 가지고 있는 데이터 중 이상치들이 있는 데이터는 삭제하고 다시 모델들을 돌려보자.</p>
<br>

<p><strong>📌 boxplot의 IQR을 이용한 이상치들의 인덱스를 반환하는 함수</strong></p>
<pre><code class="language-python">def get_outlier(df = None, column = None, weight = 1.5):
    fraud = df[df[&#39;Class&#39;] == 1][column] # fraud 데이터에 대해서만 이상치를 찾아볼 예정
    quantile_25 = np.percentile(fraud.values, 25)
    quantile_75 = np.percentile(fraud.values, 75)

    iqr = quantile_75 - quantile_25
    iqr_weight = iqr * weight
    lowest_val = quantile_25 - iqr_weight
    highest_val = quantile_75 + iqr_weight

    outlier_index = fraud[(fraud &lt; lowest_val) | (fraud &gt; highest_val)].index

    return outlier_index</code></pre>
<pre><code class="language-python">outlier_index = get_outlier(df = raw_data, column= &#39;V14&#39;, weight = 1.5)

raw_data_copy.drop(outlier_index, axis = 0, inplace = True)

# 이상치를 삭제하기 위함 -&gt; 이후 y만 추출
raw_data.drop(outlier_index, axis = 0, inplace = True)

X = raw_data_copy
y = raw_data.iloc[:, -1] # 이상치를 제거한 y

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2,
                                                    stratify=y,
                                                    random_state=13)</code></pre>
<p>위 함수를 이용해 이상치들을 제거하고 데이터를 다시 분리시킨 후, 모델들을 돌려본 결과는 아래와 같다.</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/d1e561e9-c10c-4992-b59d-e1b7061e23dd/image.png" alt=""></p>
<p>사알짝 좋아진 것 같지만 아직 아쉽긴 하다. 무엇을 더 할 수 있을까?</p>
<p><br><br></p>
<h2 id="네번째-시도---데이터-불균형-해소">네번째 시도 - 데이터 불균형 해소</h2>
<p>우리가 가지고 있는 데이터는 불균형이 심한 상태였다.
두 클래스의 분포를 강제로 맞춰보는 작업을 해보자.</p>
<p>클래스의 분포를 맞추는 방법에도 여러가지가 있는데, 먼저 <code>언더샘플링</code>과 <code>오버샘플링</code>이 있다.</p>
<ul>
<li><p>언더 샘플링 : 많은 수의 데이터를 적은 수의 데이터로 조정</p>
</li>
<li><p>오버 샘플링 : 적은 수의 데이터를 많은 수의 데이터로 조정</p>
<ul>
<li><p>대표적으로 <code>SMOTE</code> 방식이 있는데  <code>imblanced-learn</code>이라는 파이썬 패키지를 이용하면 됨.</p>
</li>
<li><p><code>!pip install imbalanced-learn</code></p>
</li>
<li><p><code>SMOTE</code>
<a href="https://www.kaggle.com/code/rafjaa/resampling-strategies-for-imbalanced-datasets/notebook"><img src="https://velog.velcdn.com/images/castle_mi/post/3445e1a7-4871-4ba5-9fb1-2107a07bafd2/image.png" alt=""></a></p>
<p>: 간단히 말하면 적은 데이터 세트에 있는 개별 데이터를 KNN으로 찾아서 데이터의 분포 사이에 새로운 데이터를 만드는 방식.</p>
</li>
</ul>
</li>
</ul>
<pre><code class="language-python">from imblearn.over_sampling import SMOTE

smote = SMOTE(random_state=13)
X_train_over, y_train_over = smote.fit_resample(X_train,y_train)</code></pre>
<p>Train 데이터 셋을 <code>SMOTE</code>를 이용해 resample한 데이터를 <code>X_train_over</code>, <code>y_train_over</code>에 넣어주었다.</p>
<p>이때, 데이터를 조작하는 것은 Train 데이터에 한해서라는 것을 주의해야한다. Test 데이터는 왜곡되면 안되기 때문에 어떠한 작업도 하지 않고 그냥 모델에 적용!만 할 것.</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/e7eaf972-3954-4a0e-84ee-636f667b6618/image.png" alt=""></p>
<p><code>SMOTE</code>작업을 통해 클래스의 분포가 같아졌다.</p>
<p>이를 모델들에 돌려본 결과는 아래와 같다.</p>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/4c1a263a-b636-49c6-813b-232907459807/image.png" alt=""></th>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/3bac3df0-4372-4862-b248-b3e3d322dd57/image.png" alt=""></th>
</tr>
</thead>
</table>
<p>우오옹!! 모든 모델들에 대해 성능이 향상된 것 같다 😆 
비록 <code>Precision</code>이 떨어진 것도 보이지만, 무엇을 선택할 건지는 데이터 분석가의 몫 ㅎㅎ</p>
<br>

<p>우리가 배운 것들을 적용시켜보는 실습은 이정도에서 마무리하도록 하겠다.</p>
<p><br><br></p>
<hr>
<h1 id="nlpnatural-language-processing-자연어-처리">NLP(Natural Language Processing, 자연어 처리)</h1>
<h2 id="환경-만들기">환경 만들기</h2>
<ol>
<li><pre><code class="language-python">!pip install konlpy
!pip install tweepy==3.10.0
!conda install -y -c conda-forge jpype1==1.0.2
!conda install -y -c conda-forge wordcloud
!conda install -y nltk</code></pre>
</li>
</ol>
<p>cmd창 또는 JupyterNotebook, VSCode 등 자신이 사용하고 있는 프로그램에 위 패키지들을 설치시켜주어야한다.</p>
<ol start="2">
<li><pre><code class="language-python">import nltk
nltk.download()</code></pre>
</li>
</ol>
<p>또한 위 코드를 실행시키면 아래와 같은 창이 하나 뜨는데
<img src="https://velog.velcdn.com/images/castle_mi/post/ee5b9539-45a4-4d0f-bc89-2fd239ba3c02/image.png" alt=""></p>
<p>[All Package] 를 누르고 Identifier 중에서 <code>punkt</code>와 <code>stopwords</code>를 다운로드 해주면 된다.</p>
<p>그리고 X표를 눌러 빠져나온다.</p>
<br>

<ol start="3">
<li><pre><code class="language-python">from konlpy.tag import Okt
</code></pre>
</li>
</ol>
<p>t = Okt()</p>
<pre><code>위 코드가 정상 실행되면 환경 세팅은 끝났다 :)

&lt;br&gt;

## 미리보기
자연어처리에서 사용할 수 있는 형태소 분석기에는 여러 종류가 있는 것 같다. 

그 중 ```Kkma```, ```Hannanum```, ```Okt```만 가볍게 살펴보자.

순서는 동일하게 분석기 호출 -&gt; 문장 입력 으로 이루어진 것 같다.

그리고 함수는 아래와 같다.
   - ```pos``` : 형태소와 품사 반환
   - ```nouns``` : 명사 반환
   - ```morphs``` : 형태소 반환

이때 형태소(morpheme)는 의미를 가지는 가장 작은 단위로, &#39;되었습니다.&#39; 라는 어절(띄어쓰기 되는 단위)도 &#39;되(동사)&#39;, &#39;었(시제 어미)&#39;, &#39;습니다(종결 어미)&#39;, &#39;.&#39; 로 형태소가 구분된다.

```python
from konlpy.tag import Kkma

kkma = Kkma() # 분석기 호출
kkma.pos(&#39;한국어 분석을 시작합니다.&#39;) #문장 입력</code></pre><blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/2afd7dc0-de30-4c38-a532-0da9e95df549/image.png" alt=""></p>
<pre><code class="language-python">kkma.morphs(&#39;한국어 분석을 시작합니다.&#39;)</code></pre>
<blockquote>
<p> 💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/580cec83-4013-4942-af1c-05eb28a1f4a0/image.png" alt=""></p>
<p>위와 같이 다른 분석기들도 동일한 결과를 출력해준다.</p>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/ba38e8bf-5d68-4776-be54-dec3096d009b/image.png" alt=""> Hannanum</th>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/a6965714-9325-42aa-942b-401faf73ea93/image.png" alt=""> Okt</th>
</tr>
</thead>
</table>
<br>

<h2 id="워드클라우드">워드클라우드</h2>
<pre><code class="language-python">from wordcloud import WordCloud, STOPWORDS
import numpy as np
from PIL import Image</code></pre>
<p>워드클라우드에 대해 간단하게 살펴보자.
<a href="https://m.blog.naver.com/kiddwannabe/221244283991"><img src="https://velog.velcdn.com/images/castle_mi/post/0369c13f-d42c-4733-b427-c0752af24e28/image.png" alt=""></a></p>
<p>워드클라우드는 위와 같이 텍스트에서 단어의 빈도수에 따라 해당 단어를 크게 표시해놓은 그림이다.</p>
<p>한 번, 앨리스 동화책 이야기를 앨리스 그림 위에 워드클라우드를 그려보자.</p>
<p><a href="https://github.com/PinkWink/ML_tutorial/tree/master/nltk_dataset">https://github.com/PinkWink/ML_tutorial/tree/master/nltk_dataset</a></p>
<p>위 링크에서 alice.txt와 alice_mask.png를 다운받아 사용했다.</p>
<br>

<p><strong>다운받은 데이터 확인하기.</strong></p>
<ol>
<li>텍스트<pre><code class="language-python">text = open(&#39;./data/06_alice.txt&#39;).read()
print(text)</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<img src="https://velog.velcdn.com/images/castle_mi/post/9d993863-eea8-4305-bdc3-1f49fa940af8/image.png" alt=""></li>
</ol>
<br>

<ol start="2">
<li>이미지<pre><code class="language-python">alice_mask = np.array(Image.open(&#39;./data/06_alice_mask.png&#39;))</code></pre>
파이썬에서 이미지를 읽을 때는 여러 방법이 있지만 픽셀값으로 변환시킨 numpy array를 matplotlib을 통해 그리는 방법이 있다. 위 코드가 픽셀 값으로 변환시킨 과정이고</li>
</ol>
<pre><code class="language-python">import matplotlib.pyplot as plt

plt.figure(figsize = (8,8))
plt.imshow(alice_mask, cmap = plt.cm.gray)</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/d1e224bd-dd1f-4ffb-a330-de777d3f8a74/image.png" alt=""></p>
<p>위 코드를 통해 확인할 수 있다.</p>
<p><br><br></p>
<p>워드클라우드를 하기 전, 큰 의미가 없는 단어들은 제외하고 그림을 그릴 필요가 있다. 
큰 의미가 없는 단어 토큰들이 모아져있는 것이 바로 아까 다운받은 <code>STOPWORDS</code>이다.</p>
<pre><code class="language-python">stopwords = set(STOPWORDS)
stopwords</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/8b236c6c-6e39-4547-ae55-3c0454aa410b/image.png" alt=""></p>
<p>그리고 어떤 단어를 더 추가하고 싶다면 아래와 같이 하면 된다.</p>
<pre><code class="language-python">stopwords.add(&#39;said&#39;) </code></pre>
<p>alice.txt에는 &#39;said&#39;라는 단어도 많이 나오는데, 큰 의미가 없어 이 또한 포함시켜주었다.</p>
<p>이제 <code>WordCloud</code>를 이용하여 alice.txt의 단어 빈도수를 출력해보자. </p>
<pre><code class="language-python">wc = WordCloud(
    background_color=&#39;white&#39;, # 배경색
    max_words= 2000,  # 최대 단어 수
    mask=alice_mask,  # 바탕 배경 지정
    stopwords=stopwords # 불필요한 단어
)

wc = wc.generate(text)
wc.words_ # 각 단어들의 발생빈도를 반환해줌</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/17b1d307-0b51-495c-ab8e-f22f97b40d8c/image.png" alt=""></p>
<p>그리고 이를 그림으로 그리면 아래와 같다.</p>
<pre><code class="language-python">plt.figure(figsize = (12,12))
plt.imshow(wc)
plt.axis(&#39;off&#39;)
plt.show()</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/ab9eef4c-ecae-49ed-ae08-d37197144b7a/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[머신러닝] Boosting Algorithm(KNN, GBM, XGBoost, LGBM) ]]></title>
            <link>https://velog.io/@castle_mi/%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-Boosting-AlgorithmKNN-GBM-XGBoost-LGBM</link>
            <guid>https://velog.io/@castle_mi/%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-Boosting-AlgorithmKNN-GBM-XGBoost-LGBM</guid>
            <pubDate>Wed, 27 Sep 2023 07:15:44 GMT</pubDate>
            <description><![CDATA[<p>✍🏻 26일 공부 이야기.</p>
<p>오늘 실습한 코드는 아래 깃허브 사진을 클릭하면 이동합니다!
<a href="https://github.com/castlemi99/ZeroBaseDataSchool/blob/main/Machine%20Learning/9.%20Boosting%20Algorithm.ipynb"><img src="https://velog.velcdn.com/images/castle_mi/post/23837f56-2164-4cf5-94ba-82e4d1702886/image.png" alt=""></a></p>
<br>

<h2 id="boosting-algorithm">Boosting Algorithm</h2>
<p><code>Boosting Algorithm</code>에는 앞서 배운 <code>Voting</code>과 <code>Bagging</code>그리고 <code>Boosting</code>과 <code>Stacking</code>등이 있다.</p>
<p><code>Voting</code>, <code>Bagging</code>은 여러 개의 분류기의 예측값을 투표를 통해 최종 예측 결과를 결정하는 방식이었는데  <code>Boosting</code>은 무엇일까?</p>
<p><code>Boosting</code>은 여러 개의 약한 분류기가 <strong>순차적으로 학습을 하면서</strong> 앞에서 학습한 분류기가 예측이 틀린 데이터에 대해 다음 분류기가 가중치를 인가해서 학습을 이어 진행하는 방식이다.
이때 약한 분류기란, 성능은 떨어지지만 속도가 빠른 분류기를 뜻한다.</p>
<p><code>Boosting</code>은 그래디언트부스트, XGBoost, LightGBM 등이 있다.</p>
<p>와인 데이터를 통해 이들을 실습해보자.</p>
<h3 id="와인-데이터">와인 데이터</h3>
<p>앞서 계속 실습했던 <code>taste</code> 컬럼에 대한 예측을 여러 모델들을 학습시키고 평가해보자. 이번에는 각각 모델을 돌리는 것이 아닌 한 번에 돌리는 방법이다.</p>
<pre><code class="language-python">from sklearn.ensemble import AdaBoostClassifier, GradientBoostingClassifier, RandomForestClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.linear_model import LogisticRegression

models = []
models.append( (&#39;AdaBoostClassifier&#39;, AdaBoostClassifier()))
models.append( (&#39;GradientBoostingClassifier&#39;, GradientBoostingClassifier()))
models.append( (&#39;RandomForestClassifier&#39;, RandomForestClassifier()))
models.append( (&#39;DecisionTreeClassifier&#39;, DecisionTreeClassifier()))
models.append( (&#39;LogisticRegression&#39;, LogisticRegression()))</code></pre>
<p>위 <code>models</code>변수에 모델명과 모델을 저장해준다.</p>
<pre><code class="language-python">from sklearn.model_selection import KFold, cross_val_score

results = []
names = []

for name, model in models:
    kfold = KFold(n_splits=5, random_state=13, shuffle= True) # shuffle : 데이터 분할 전 데이터를 섞어라.
    cv_results = cross_val_score(model, X_train, y_train, cv = kfold, scoring=&#39;accuracy&#39;)

    results.append(cv_results)
    names.append(name)

    print(name, cv_results.mean(), cv_results.std())</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/c9ca43a4-6e08-4a10-b46e-cbfe2057afc6/image.png" alt=""></p>
<p>그리고 교차검증을 실시하며 각 모델의 성능을 추출하고 평균을 통해 최종 성능값을 확인해보기로 했다. </p>
<p>아래와 같이 시각화를 통해 확인할 수도 있다.
<img src="https://velog.velcdn.com/images/castle_mi/post/b6db7623-9408-475c-a69d-133235eccdc1/image.png" alt=""></p>
<p>지금 와인 데이터에 대해서는 <code>Random Forest</code>의 성능이 제일 좋아보이지만, 이 모델이 무조건 좋다!가 아니다. 데이터의 특성마다 모델들의 성능은 달라진다는 것을 명심하자!</p>
<br>

<h3 id="knn">KNN</h3>
<p><a href="https://scikit-learn.org/stable/modules/generated/sklearn.neighbors.KNeighborsClassifier.html"><img src="https://velog.velcdn.com/images/castle_mi/post/c5e76429-344d-48a8-b556-86bfbe208ce4/image.png" alt="">
</a>
<code>KNN</code>은 K-Nearest Neighbor의 줄임말로, 지도 학습 알고리즘 중 하나인 K-최근접 이웃이다.</p>
<blockquote>
<p> 💡여기서 복습! 지도학습이란?
: 모델을 학습시킬 때 정답지까지 같이 주고 학습시키는 것을 말했다.</p>
</blockquote>
<p><code>KNN</code>의 알고리즘은 간단하다. 데이터로부터 k번째까지 가까운 데이터들의 클래스를 확인하고 투표를 통해 해당 데이터의 클래스를 결정하는 방식이다.</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/75effc41-4c61-445e-94c5-0718571235b6/image.png" alt=""></p>
<p>예를 들어 N의 클래스를 결정하고자 한다고 해보자.</p>
<ul>
<li>k = 1일 때, N과 가장 가까운 데이터는 C(○)이므로 N은 ○가 된다.</li>
<li>k = 3일 때, N과 가장 가까운 데이터는 C(○), D(△), E(△)이므로 N은 △가 된다.</li>
<li>k = 5일 때, N과 가장 가까운 데이터는 C(○), D(△), E(△), A(○), B(○)이므로 N은 ○가 된다.</li>
</ul>
<p>이런 느낌인 것이다. 즉 <code>KNN</code>은 k값에 따라 결과가 바뀔 수도 있다.</p>
<p><code>KNN</code>을 사용하기 위해선 또 필요한게 있는데</p>
<p>바로 <strong>거리 계산</strong>.</p>
<p>일반적으로 알고 있는 두 점 사이의 거리를 계산하는 공식을 이용하면 되지만 주의할 점이 하나 있다.</p>
<p>x축과 y축의 변화량이 다르다면 단위에 따라 계산되는 거리가 달라지므로 표준화를 하는 것이 좋다고 한다.</p>
<h4 id="iris-데이터">iris 데이터</h4>
<p>iris 데이터를 통해 실습해보자.</p>
<pre><code>from sklearn.neighbors import KNeighborsClassifier

knn = KNeighborsClassifier(n_neighbors=5) # default = 5
knn.fit(X_train, y_train)

from sklearn.metrics import accuracy_score

pred = knn.predict(X_test)
accuracy_score(y_test, pred)</code></pre><blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/697af2fc-66b8-4837-8609-0061519ca29b/image.png" alt=""></p>
<p>이때 <code>KNN</code>은 그냥 주어진 데이터로부터 가장 가까운 클래스가 무엇인지 찾는 과정이기 때문에 이전 모델과 같이 실제로 학습(fit)을 하지는 않는다. 하지만 sklearn 상 fit을 시켜주어야하기 때문에 코드는 실행시켜주어야한다.</p>
<p><br><br></p>
<h3 id="gbmgradient-boosting-machine">GBM(Gradient Boosting Machine)</h3>
<p>여러 개의 약한 분류기를 순차적으로 학습-예측하면서 잘못 예측한 데이터에 가중치를 부여해 오류를 개선해가는 방식이 <code>Boosting Algorithm</code>이라 했다. <code>GBM</code>은 가중치를 업데이트할 때 경사하강법을 이용하는 것을 말한다.</p>
<pre><code class="language-python">from sklearn.ensemble import GradientBoostingClassifier
from sklearn.metrics import accuracy_score

gb_clf = GradientBoostingClassifier(random_state=13)
gb_clf.fit(X_train, y_train)
gb_pred = gb_clf.predict(X_test)

accuracy_score(y_test, gb_pred)</code></pre>
<p>똑같이 <code>GradientBoostingClassifier</code>을 임포트하여 사용할 수 있다.</p>
<pre><code class="language-python"># GridSearch
from sklearn.model_selection import GridSearchCV

params = {
    &#39;n_estimators&#39; : [100,500],
    &#39;learning_rate&#39; : [0.05, 0.1]
}

## verbose 옵션 : 수행 결과 메세지 출력 여부
## 0(default) : 출력 x, 1 : 간단한 메세지 출력, 2 : 하이퍼파라미터별 메세지 출력
grid = GridSearchCV(gb_clf, param_grid=params, cv = 2, verbose=2, n_jobs=-1)
grid.fit(X_train, y_train)

accuracy_score(y_test, grid.best_estimator_.predict(X_test))</code></pre>
<p>또한 <code>GridSearchCV</code>도 할 수 있는데 <code>GBM</code>은 속도가 매우 느리다는 단점이 있다.</p>
<p>HAR 데이터로 실습해보려고 했는데 15분이 넘게 돌아가고 있어서 output을 확인하지 못했다 😭</p>
<p>속도가 빠른 <code>Boosting Algorithm</code>은 없을까?</p>
<br>

<h3 id="xgboost">XGBoost</h3>
<p><a href="https://wooono.tistory.com/97">🖱️ 보다 자세한 내용</a></p>
<p><code>!pip install xgboost</code>
<a href="https://xgboost.readthedocs.io/en/stable/python/python_api.html#module-xgboost.sklearn"><img src="https://velog.velcdn.com/images/castle_mi/post/9be8064a-f4d2-467a-a0d9-ee5915af8023/image.png" alt=""></a></p>
<p>트리 기반의 앙상블 학습에서 주목받고 있는 알고리즘이다.
<code>GBM</code> 기반의 알고리즘인데, <code>GBM</code>의 느린 속도를 다양한 규제를 통해 해결했다.</p>
<p>특히 병렬 학습이 가능해졌으며 Regression과 Classification 문제를 모두 지원한다.</p>
<p><code>XGBoost</code>는 반복 수행시마다 내부적으로 학습 데이터와 검증 데이터를 교차검증을 수행하는데 교차검증을 통해 최적화되면 반복을 중단하는 조기 중단 기능을 가지고 있다.</p>
<br>

<p><strong>주요 파라미터</strong></p>
<ul>
<li><code>nthread</code> : CPU의 실행 스레드 개수를 조정하는 파라미터. 디폴트는 전체 스레드를 사용함.</li>
<li><code>eta</code> : GBM의 학습률</li>
<li><code>n_estimators</code></li>
<li><code>max_depth</code></li>
</ul>
<p><strong>조기 중단을 하기 위한 파라미터</strong></p>
<ul>
<li><code>early_stopping_rounds</code> : 조기 중단할 수 있는 최소 반복 횟수</li>
<li><code>eval_set</code> : 평가를 수행하는 별도의 검증 데이터 셋</li>
<li><code>eval_metric</code> : 평가를 수행하는 성능 기준</li>
</ul>
<br>


<p><code>XGBoost</code>를 사용하기 위한 몇 가지 주의사항이 있다.</p>
<p>먼저, fit시킬 때 <code>xgb.fit(X_train, y_train)</code>의 형태를 생각할 것이다.</p>
<p>하지만 이 때 <code>X_train</code>과 <code>y_train</code>을 그대로 사용하면 에러를 보게 될 것이다.</p>
<p>먼저 <code>XGBoost</code>에는 numpy array 값의 형태로 데이터를 넣어주어야하기 때문에 <code>X_train</code> -&gt; <code>X_train.values</code> 로 변경해주어야한다. </p>
<blockquote>
<p>ValueError: Invalid classes inferred from unique values of <code>y</code>.</p>
</blockquote>
<p>또한 위와 같은 에러를 보게 되었다면 y_train 안에 데이터들이 0부터 시작하는 것이 아니여서 그런 것이니 아래와 같은 작업을 실행시킨 후 다시 fit을 시켜주면 된다.</p>
<pre><code class="language-python">from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
y_train = le.fit_transform(y_train)
y_test = le.fit_transform(y_test)</code></pre>
<p><a href="https://stackoverflow.com/questions/71996617/invalid-classes-inferred-from-unique-values-of-y-expected-0-1-2-3-4-5-got">🖱️ 해당 에러 stack overflow 링크</a></p>
<br>

<p>따라서 최종 모델링하는 코드는 아래와 같다.</p>
<pre><code class="language-python">from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
y_train = le.fit_transform(y_train)
y_test = le.fit_transform(y_test)

from xgboost import XGBClassifier

xgb = XGBClassifier(n_estimators = 400, learning_rate = 0.1, max_depth=3)
xgb.fit(X_train.values, y_train) # xgboost에는 numpy values값의 형태로 넣어주어야한다.

accuracy_score(y_test, xgb.predict(X_test))</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/03b8a653-30d0-4ab5-a2dd-5b62e350b8f6/image.png" alt=""></p>
<br>

<p>그럼 조기 종료 조건은 어떻게 할 수 있는가.</p>
<pre><code class="language-python"># 조기 종료 조건과 검증 데이터 지정

evals = [(X_test.values, y_test)] # 검증 데이터 셋. 이번 실습에서는 테스트 셋을 검증 셋으로 설정했다.

xgb = XGBClassifier(n_estimators = 400, learning_rate = 0.1, max_depth=3)
xgb.fit(X_train.values, y_train, early_stopping_rounds=10, eval_set=evals)

accuracy_score(y_test, xgb.predict(X_test))</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/3beafca3-c6b8-4168-a4c4-83c666aa5df3/image.png" alt=""></p>
<p>위와 같이 조기 종료 조건 파라미터를 설정해주면 된다.
성능이 조금 떨어질 수도 있겠지만 시간이 단축된다는 장점이 있다.</p>
<p><br><br></p>
<h3 id="lightgbm">LightGBM</h3>
<p><a href="https://potato-potahto.tistory.com/entry/Light-GBM-%EC%84%A4%EB%AA%85%ED%8A%B9%EC%A7%95%ED%95%98%EC%9D%B4%ED%8D%BC%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0%EC%84%A4%EC%B9%98-%EC%82%AC%EC%9A%A9%EB%B0%A9%EB%B2%95">🖱️ 보다 자세한 내용</a></p>
<p><code>!pip install lightgbm</code></p>
<p><code>LightGBM</code>또한, <code>XGBoost</code>와 함께 주목받고 있는 알고리즘 중 하나이다. <code>LightGBM</code>은 속도가 굉장히 빠르지만 적은 수의 데이터에는 어울리지 않는다고 한다.(일반적으로 10000건 이상의 데이터 필요)</p>
<p><code>XGBoost</code>는 CPU만 쓰지만 <code>LightGBM</code>은 GPU 버전도 존재한다.(아마 버전을 따로 설치해야하는 것 같다.)</p>
<pre><code class="language-python">from lightgbm import LGBMClassifier 

lgbm = LGBMClassifier(n_estimators = 400, learning_rate = 0.1, max_depth=3)
lgbm.fit(X_train.values, y_train)

accuracy_score(y_test, lgbm.predict(X_test.values))</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/7257f856-8037-4a3a-b222-9f20cc33d828/image.png" alt=""></p>
<p>속도가 굉장히 빨랐다!</p>
<p><br><br></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[머신러닝] Logistic Regression / 앙상블 기법 ]]></title>
            <link>https://velog.io/@castle_mi/%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-Logistic-Regression-%EC%95%99%EC%83%81%EB%B8%94-%EA%B8%B0%EB%B2%95</link>
            <guid>https://velog.io/@castle_mi/%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-Logistic-Regression-%EC%95%99%EC%83%81%EB%B8%94-%EA%B8%B0%EB%B2%95</guid>
            <pubDate>Mon, 25 Sep 2023 10:09:57 GMT</pubDate>
            <description><![CDATA[<p>✍🏻 25일 공부 이야기.</p>
<p>👀 오늘 공부한 내용의 실습 코드는 아래 사진을 클릭하면 보실 수 있습니다:)</p>
<p><a href="https://github.com/castlemi99/ZeroBaseDataSchool/blob/main/Machine%20Learning/8.%20Logistic%20Regression.ipynb"><img src="https://velog.velcdn.com/images/castle_mi/post/ad66659b-41e5-45c0-9ab5-50df45a13856/image.png" alt=""></a></p>
<br>

<h1 id="logistic-regression">Logistic Regression</h1>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/1f3850f6-0232-437c-8c6e-cc17bbd8fd3b/image.png" alt=""></p>
<p>Logistic Regression 은 위와 같이 0과 1을 잘 분류해준다는 특성때문에 분류 문제에서 많이 사용된다. </p>
<p>추가로, $g(z)$ 함수는 시그모이드 함수이다.</p>
<br>

<p>덧붙여 <code>Decision Boundary</code>와 <code>Cost Function</code>에 대한 개념 소개를 더 하자면</p>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/dc70bd92-6af8-49d0-9bef-7ddd3282e501/image.png" alt=""></th>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/22c8e35c-6515-46a8-8620-bf6705a16b67/image.png" alt=""></th>
</tr>
</thead>
</table>
<p>위와 같이 클래스가 바뀌는 지점을 <code>Decision Boundary</code>라고 한다.</p>
<br>

<p>그리고 지난 시간에 정리했던 <code>Cost Function</code>은 MSE를 사용했기 때문에 2차식이 나와(convex 형태) 위로 볼록하거나 아래로 볼록한 단일 형태의 그래프가 나왔지만, Logistic Regression에서는 아래와 같은 <code>Cost Function</code>을 사용하기 때문에 non-convex한 형태가 나올 수도 있지만 $log$가 취해진 덕분에 미분 후 최소값을 찾는 과정은 우리가 알고있는대로 쉽게 구할 수 있을 것이다.</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/d7e30371-c1b0-4b5c-b30a-d7bfe40ba877/image.png" alt=""></p>
<p><br><br></p>
<h2 id="와인-데이터">와인 데이터</h2>
<p>그러면 앞서 실습했던 와인 데이터를 이용해 Logistic Regression도 실습해보자.</p>
<pre><code class="language-python">
from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X, y , test_size= 0.2, random_state=13)


from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score

lr = LogisticRegression(solver= &#39;liblinear&#39; , random_state=13)
lr.fit(X_train, y_train)

y_pred_tr = lr.predict(X_train)
y_pred_test  = lr.predict(X_test)

print(&quot;Train acc : &quot;, accuracy_score(y_train, y_pred_tr))
print(&quot;Test acc : &quot;, accuracy_score(y_test, y_pred_test))</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/13aef862-d6e7-4f9a-a0de-c055f4e2411b/image.png" alt=""></p>
<p> Logistic Regression의 옵션 중 <code>solver</code>가 있는데 이는 최적화할 때 어떤 알고리즘으로 할 것인가에 대한 사항이다. 보통 <code>liblinear</code>는 데이터가 작고 일대일 방식의 데이터에 많이 사용되며 <code>sag</code>나 <code>saga</code>는 데이터가 크고 다중 클래스 문제에 많이 사용된다고 한다. (다중 클래스 문제에는 <code>newton-cg</code>와 <code>lbfgs</code>도 있다.)</p>
<p>만약 파이프라인을 구축해본다고 하면 아래와 같이 할 수 있다.</p>
<pre><code class="language-python">from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler

estimators = [
    (&#39;scaler&#39;, StandardScaler()),
    (&#39;clf&#39;, LogisticRegression(solver=&#39;liblinear&#39;, random_state=13))
]

pipe = Pipeline(estimators)

pipe.fit(X_train, y_train)

y_pred_tr = pipe.predict(X_train)
y_pred_test  = pipe.predict(X_test)

print(&quot;Train acc : &quot;, accuracy_score(y_train, y_pred_tr))
print(&quot;Test acc : &quot;, accuracy_score(y_test, y_pred_test))</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/2839609b-6418-40f7-9909-54fca24606ad/image.png" alt=""></p>
<br>

<p>이전 시간에 분류 문제로 실습했던 <code>Decision Tree</code>와 함께 <code>roc-curve</code>를 그려 비교해보자. </p>
<pre><code class="language-python">from sklearn.tree import DecisionTreeClassifier

wine_tree = DecisionTreeClassifier(max_depth=2, random_state=13)
wine_tree.fit(X_train, y_train)

models = {
    &#39;logistic regression&#39; : pipe, 
    &#39;decision tree&#39; : wine_tree
}</code></pre>
<p><code>Decision Tree</code>도 fit시켜주고 <code>models</code> 에 두 모델을 저장해주면 </p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/61fbd5da-ccd5-4026-8baa-4bd6cf49ac09/image.png" alt=""></p>
<p>위와 같이 <code>.items()</code>를 호출해 각 모델을 부를 수 있다.</p>
<pre><code class="language-python">from sklearn.metrics import roc_curve
import matplotlib.pyplot as plt

plt.figure(figsize = (10,8))
plt.plot([0,1], [0,1], label = &#39;random_guess&#39;)

for model_name, model in models.items():
    pred = model.predict_proba(X_test)[:, 1] # 첫번째 컬럼은 0일 확률이므로 1일 확률인 두번째 컬럼을 추출해야함
    fpr, tpr, thresholds = roc_curve(y_test, pred)

    plt.plot(fpr, tpr, label = model_name)

plt.grid()
plt.legend()
plt.show()</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/b8d7db3d-d795-4654-9aa8-183596afd7ff/image.png" alt=""></p>
<p>이를 이용해 for문으로 각 모델의 <code>fpr</code>, <code>tpr</code>을 그려보면 위와 같이 출력되는데 물론 이번 한 번의 과정을 통해 <code>Logistic Regression</code> 성능이 더 뛰어나다고는 할 순 없지만 지금 이 데이터에서는 <code>Decision Tree</code>보단 <code>Logisic Regression</code>의 성능이 더 좋아보인다.</p>
<p><br><br></p>
<h2 id="pima-인디언-당뇨병-예측">PIMA 인디언 당뇨병 예측</h2>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/3871de8b-fbe7-4434-9acf-5e390789b4f6/image.png" alt=""></p>
<p>50년대 까지 PIMA 인디언들은 당뇨가 없었는데 20세기 말 갑자기 인구의 50%가 당뇨에 걸리게 되면서 데이터 분석에 흥미로운 주제 중 하나로 꼽히게 되었다. </p>
<p>본 데이터는 캐글에 있으며 컬럼별 설명은 위 사진과 같다.</p>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/b759ab97-e961-404c-95c5-fe126c124a16/image.png" alt=""></th>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/dcb81107-0430-4133-bf44-5b2fe40c6dd7/image.png" alt=""></th>
</tr>
</thead>
</table>
<p>많은 작업을 하면서 <code>int</code>형을 자동으로 <code>float</code>형으로 변환하여 작업해주는 것들도 있지만 혹시 모르기 때문에 모두 다 <code>float</code>형으로 바꾼 후 작업했다.  </p>
<br>

<p>이번 실습을 하면서 관심있게 봐주었으면 하는 부분은 바로 <strong>결측치 처리 파트</strong>이다.</p>
<h3 id="결측치를-찾아줘">결측치를 찾아줘!</h3>
<p>앞서 <code>info()</code>를 통해 본 것으로는 결측치가 없어보였다. 하지만 아래 코드를 보자.
<img src="https://velog.velcdn.com/images/castle_mi/post/146ace1e-dea6-4162-9677-212eb0a1b5a1/image.png" alt=""></p>
<p>다른 컬럼들은 값이 0일 수도 있다고 생각이 들지만,
<code>Glucose(포도당 부하 검사 수치)</code>, <code>BloodPressure(혈압)</code>, <code>SkinThickness(팔 삼두근 뒤쪽의 피하지방 측정값)</code>, <code>BMI(체질량지수)</code> 컬럼들이 0인 것은 뭔가 좀 이상하다!</p>
<p>바로 결측값인 것이다.</p>
<p>결측치는 데이터를 수집하고 가공한 사람에 따라 정의를 다르게 한다. Nan, Null, 0, - 등 여러가지가 있다. 그러므로 우리는 마냥 <code>null</code>이 있냐 없냐만 보고 넘어갈 것이 아닌 보다 꼼꼼하게 결측치를 확인할 줄 알아야한다. </p>
<p>그리고 이 결측치들을 어떻게 처리할 것인가도 고민해보아야 한다. </p>
<blockquote>
<p>그냥 해당 데이터를 삭제할 것인지, 아니면 결측치를 이전 데이터의 값으로 대체할 것인지, 아니면 평균값/중앙값 등으로 대체할 것인지...</p>
</blockquote>
<p>데이터가 어떤 성향을 가졌는지 보고 전문가의 조언을 받아도 되고 여러 시도를 해보며 어떤 방향으로 접근했을 때 성능이 좋게 나오는지 확인 해보아도 된다.</p>
<pre><code class="language-python">zero_features = [&#39;Glucose&#39;, &#39;BloodPressure&#39;, &#39;SkinThickness&#39;, &#39;BMI&#39;]
PIMA[zero_features] = PIMA[zero_features].replace(0, PIMA[zero_features].mean())</code></pre>
<p>지금은 그냥 &lt;평균값&gt;으로 결측치를 대체하고 실습을 진행해보겠다.</p>
<h3 id="모델링">모델링</h3>
<pre><code class="language-python"># 데이터 분리
from sklearn.model_selection import train_test_split

X = PIMA.drop([&#39;Outcome&#39;], axis = 1)
y = PIMA[&#39;Outcome&#39;]

X_train, X_test, y_train, y_test = train_test_split(X, y , test_size= 0.2, 
                                                    stratify=y, 
                                                    random_state=13)

# 모델링                                                 from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression

estimators = [
    (&#39;scaler&#39;, StandardScaler()),
    (&#39;clf&#39;, LogisticRegression(solver=&#39;liblinear&#39;, random_state=13))
]

pipe_lr = Pipeline(estimators)
pipe_lr.fit(X_train, y_train)
pred = pipe_lr.predict(X_test)

# 성능 평가
from sklearn.metrics import accuracy_score, recall_score, precision_score
from sklearn.metrics import roc_auc_score, f1_score

print(&#39;Accuracy : &#39;, accuracy_score(y_test, pred))
print(&#39;Recall : &#39;, recall_score(y_test, pred))
print(&#39;Precision : &#39;, precision_score(y_test, pred))
print(&#39;ROC AUC score : &#39;, roc_auc_score(y_test, pred))
print(&#39;F1 score : &#39;, f1_score(y_test, pred))</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/47ed5ad4-b5e6-42a6-96dc-fe147b264c00/image.png" alt=""></p>
<p>이제는 위 과정이 좀 익숙해졌을 것이다.</p>
<p>그럼 더 나아가 이것도 확인해보자.</p>
<h3 id="feature-importances">Feature importances</h3>
<p>각 모델의 성능을 평가하고, 쓰인 Feature들의 중요도를 추출하여 다음 모델은 어떻게 시도해볼 것인가에 대해 고민할 수 있다. </p>
<p><code>tree</code>모델들 같은 경우, <code>.feature_importances_</code> 를 호출하여 각 Feature들의 중요도를 확인할 수 있는데 <code>Logistic Regression</code>에서 이를 사용하려고 하면 </p>
<blockquote>
<p>AttributeError: &#39;LogisticRegression&#39; object has no attribute &#39;feature_importances_&#39;</p>
</blockquote>
<p>위와 같은 에러 문구를 볼 수 있을 것이다.</p>
<p><code>Logistic Regression</code>에서는 하나의 방정식을 만들어주므로 각 Feature들의 계수값이 곧 중요도가 된다. </p>
<p>계수값은 <code>.coef_</code>를 통해 확인할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/03c937b3-26a1-4e95-92e1-de874c42c174/image.png" alt=""></p>
<pre><code class="language-python">features[&#39;importance&#39;].plot(kind = &#39;barh&#39;,
                            figsize = (11,6),
                            color = features[&#39;positive&#39;].map({True : &#39;blue&#39;, False : &quot;red&quot;}))
plt.xlabel(&#39;importance&#39;)
plt.show()</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/55825532-b7c9-4cbb-a6d6-afb9c62e6a1a/image.png" alt=""></p>
<p>그리고 중요도를 시각화하여 보기 쉽게 정리하면 PIMA 인디언들의 당뇨를 예측하는데에 <code>Glucose(포도당 부하 검사 수치)</code>와 <code>BMI</code> 컬럼이 중요한 작용을 했다는 것을 확인할 수 있다.</p>
<br>

<h2 id="recall-과-precision">Recall 과 Precision</h2>
<p>모델을 평가하는 방법으로 Recall과 Precision이 있다. 이 둘은 반대 성향을 가지고 있는데 둘 다 성능이 좋아지는 방법이 있긴 하다.</p>
<p>바로 <code>threshold</code>를 조정하는 것이다.</p>
<p>하지만 이를 조정하여 Recall과 Precision이 좋아지도록 모델을 만드는 방법을 추천하지는 않는다.
왜냐하면 해당 성능이 나의 데이터에 한정적일 수도 있고 극단적인 <code>threshold</code>가 더 악영향을 가져올 수 있기 때문이다.</p>
<p>하지만 어떻게 작용될 수 있는지는 살펴보도록 하자.</p>
<h3 id="binarizer">Binarizer</h3>
<p>앞서 accuracy_score, recall_score, precision_score를 호출하여 일일이 print해서 살펴본 것과 다르게 <code>classification_report(실제값, 예측값)</code>을 이용하면 좀 더 쉽게 해당 값들을 살펴볼 수 있다.</p>
<pre><code class="language-python">from sklearn.metrics import classification_report

print(classification_report(y_test, lr.predict(X_test)))</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/65255703-e252-4d29-bd9f-98b0bbb3a040/image.png" alt=""></p>
<p>표로 아주 깔끔하게 정리하여 출력해주기도 하고</p>
<p><code>confusion_matrix</code>를 이용하면 아래와 같이 출력해준다.</p>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/53f10150-f219-4567-b8aa-f01bf5b2bc2a/image.png" alt=""></th>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/66bf550f-3158-4ee5-9105-bdc9eaa3b526/image.png" alt=""></th>
</tr>
</thead>
</table>
<p>오른쪽 표에 해당하는 개수를 표로 표현해주는 것을 볼 수 있다.</p>
<br>

<p>우리는 지금 <code>threshold</code>가 변함에 따라 <code>precision</code>과 <code>recall</code>이 어떻게 변하는가를 알아봐야한다.</p>
<p>이를 시각화를 통해 한 눈에 파악하려면 아래와 같이 하면 된다.</p>
<pre><code class="language-python">from sklearn.metrics import precision_recall_curve

plt.figure(figsize=(11,6))
pred = lr.predict_proba(X_test)[:,1] # 클래스가 1이 될 확률만 추출
precisions, recalls, thresholds = precision_recall_curve(y_test, pred)

plt.plot(thresholds, precisions[:len(thresholds)], label = &#39;precision&#39;)
plt.plot(thresholds, recalls[:len(thresholds)], label = &#39;recall&#39;)
plt.grid()
plt.legend()
plt.show()</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/4f7e39a5-23b8-4cef-a72b-90d46d5505d0/image.png" alt=""></p>
<p>두 지표는 서로 반대 성향이기 때문에 반대의 그래프를 가지고 있는 것을 볼 수 있다. </p>
<p>우리는 그동안 <code>threshold</code>가 0.5인 것을 기준으로 성능을 평가해왔었는데 그러면 <code>threshold</code>는 어떻게 바꿀 수 있는 것일까?</p>
<br>


<p><code>Binarizer(threshold = 원하는 값).fit()$</code></p>
<p>을 이용하면 된다. <code>Binarizer</code>는 이항변수화 변환에 쓰이는 라이브러리로 연속형 변수를 특정 값 기준 이하는 0, 초과는 1로 표현할 수 있게 해주는 도구이다.</p>
<pre><code class="language-python">from sklearn.preprocessing import Binarizer

binarizer = Binarizer(threshold=0.6).fit(pred_proba)
pred_bin = binarizer.transform(pred_proba)[:,1] # 클래스가 1일 때를 기준으로 0,1이 표현되는 열 추출

print(classification_report(y_test,pred_bin))</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/4c6505b0-c6e6-409d-a1c8-3d09ce5f2fa7/image.png" alt=""></p>
<p><code>threshold</code>가 0.6일 때의 <code>classification_report</code>도 확인해볼 수 있다.</p>
<p><br><br></p>
<h1 id="앙상블-기법">앙상블 기법</h1>
<p>앙상블 기법이란, 여러 개의 분류기를 생성하고 그 예측을 결합하여 정확한 최종 예측을 기대하는 기법을 말한다.</p>
<p>다양한 분류기의 예측 결과를 결합함으로써 단일 분류기보다 신뢰성이 높은 예측값을 얻는 것을 기대할 수 있다.</p>
<p>앙상블 기법에는 여러 방법이 있다.</p>
<h2 id="voting">Voting</h2>
<p><code>Voting</code> 기법이란, 여러 개의 분류기에서 예측한 값들을 투표를 통해 최종 예측값으로 선택하는 것을 말한다. </p>
<p>여기서 <strong>여러 개의 분류기를 사용했다는 점</strong>이 포인트이다. 이것이 앙상블 기법으로서의 <code>Voting</code>이고
최종 결정 단계에서의 <code>Voting</code>도 있다.</p>
<h2 id="bagging">Bagging</h2>
<p><code>Bagging</code>기법은 보통 하나의 분류기를 사용하는데, 전체 데이터 셋을 중복을 허용하여 여러 샘플링으로 나누고 각각의 데이터에서 나온 결과를 투표를 통해 최종 예측값으로 선택하는 것을 말한다.</p>
<p>이때 데이터를 각각 샘플링해서 추출하는 방식을 <code>부트스트래핑(bootstrapping) 분할 방식</code>이라 한다.</p>
<p><code>Bagging</code>기법의 대표적인 모델로 <code>Random Forest</code>가 있다. </p>
<h3 id="random-forest">Random Forest</h3>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/1ead1ce2-bb56-4121-b3ad-9f53b57d1321/image.png" alt=""></p>
<p><code>Random Forest</code>는 <code>Decision Tree</code>를 여러번 반복하여 최종 결정을 하는 모델로 앙상블 기법 중 비교적 속도가 빠르며 다양한 영역에서 높은 성능을 보여주고 있다.</p>
<p><code>부트스트래핑</code>으로 샘플링된 데이터마다 <code>Decision Tree</code>가 예측한 결과를 소프트 보팅 방식으로 최종 예측 결론을 얻는다.</p>
<p><strong>그렇다면 결정 단계에서의 Voting은 무엇일까?</strong></p>
<h2 id="결정-단계의-voting">결정 단계의 Voting</h2>
<p>하나의 데이터셋으로 여러 개의 분류기를 사용하든, 데이터셋을 여러 개로 나눈 후 하나의 분류기를 사용하든 우리에겐 많은 답안이 주어질 것이다. 이 때 많은 답들 중 다수결을 통해 최종 예측값을 선택하는 과정을 결정 단계의 <code>Voting</code>이라 한다.</p>
<p><code>Voting</code>에는 하드 보팅과 소프트 보팅으로 나뉘는데,</p>
<p><strong>1. 하드 보팅(<code>Hard Voting</code>)</strong>
하드 보팅은 분류기에서 1, 1, 0, 1이라는 답변이 나왔을 때 1을 선택하는 방식이다.</p>
<p><strong>2. 소프트 보팅(<code>Soft Voting</code>)</strong>
   소프트 보팅은 분류기에서 1, 1, 0, 1이라는 답변을 냈지만 각각의 클래스로 예측을 하게된 확률값이 있을 것이다. 이 확률값을 통해 최종 예측값을 선택하는 것이 소프트 보팅의 방식이다.</p>
<p>   예를 들어, 각각의 클래스로 예측하게 된 확률이 다음과 같다고 하자.
   1(0.9) , 1(0.8), 0(0.8), 1(0.4)
   이때 1이 될 확률은 각 예측값들의 평균을 낸 0.7(= (0.9+0.8+0.4)/3)이 될 것이고 0은 그대로 0.8일 것이다. 그러면 0이라고 택한 클래스의 확률이 더 높으므로 0을 최종 예측값으로 선택하는 방식이 소프트 보팅이다.</p>
<br>

<h2 id="harhuman-activity-recognition">HAR(Human Activity Recognition)</h2>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/ecf54daf-ab27-44d8-a0da-6342656cb10d/image.png" alt=""></p>
<p>HAR 데이터를 이용해 앙상블 기법을 실습해보자.</p>
<p>이 데이터는 사람의 몸에 디바이스를 부착하여 사람의 행동을 인지하기 위해 수집된 데이터이다.</p>
<p><a href="https://archive.ics.uci.edu/dataset/240/human+activity+recognition+using+smartphones"><img src="https://velog.velcdn.com/images/castle_mi/post/294c3f6a-e23a-4d1a-b18f-06ad84814c4e/image.png" alt=""></a></p>
<p>실제 데이터는 위 사이트에서 다운받을 수 있는데 PinkWink에 다운받아진 데이터를 이용해 실습해보도록 하자.</p>
<p>HAR 데이터는 feature들의 이름이 저장된 데이터와 실제 데이터가 있는 데이터 2종류로 구분되어 있다. 따라서 각각을 읽어들이고 데이터프레임의 컬럼 이름을 바꿔주는 작업을 해주어야한다. </p>
<p>📌 feature 이름이 저장된 데이터</p>
<pre><code class="language-python">url = &#39;https://raw.githubusercontent.com/PinkWink/ML_tutorial/master/dataset/HAR_dataset/features.txt&#39;

# \s+ : 공백 문자(스페이스, 탭, 개행 등)
feature_name_df = pd.read_csv(url, sep = &#39;\s+&#39;, header = None,
                              names = [&#39;column_index&#39;, &#39;column_name&#39;])

feature_name = feature_name_df.iloc[:,1].values.tolist() # 리스트 형태로 바꿔줌</code></pre>
<p>📌 X_train, X_test가 저장된 데이터</p>
<pre><code class="language-python">X_train_url = &#39;https://raw.githubusercontent.com/PinkWink/ML_tutorial/master/dataset/HAR_dataset/train/X_train.txt&#39;
X_test_url = &#39;https://raw.githubusercontent.com/PinkWink/ML_tutorial/master/dataset/HAR_dataset/test/X_test.txt&#39;

X_train = pd.read_csv(X_train_url, sep = &#39;\s+&#39;, header = None)
X_test = pd.read_csv(X_test_url, sep = &#39;\s+&#39;, header = None)

# 컬럼 이름 변경
X_train.columns = feature_name
X_test.columns = feature_name

## 똑같이 y_train, y_test도 읽어들이면 된다. column name은 &#39;action&#39;</code></pre>
<br>

<p>데이터를 읽어들였다면 총 4가지 작업을 해보자.</p>
<blockquote>
<ol>
<li>Decision Tree</li>
<li>Grid Search CV를 적용한 Decision Tree</li>
<li>Grid Search CV를 적용한 Random Forest</li>
<li>중요 feature들만 골라 예측한 Random Forest</li>
</ol>
</blockquote>
<br>

<h3 id="1-decision-tree">1. Decision Tree</h3>
<p>앞서 많이 했듯이 기본 옵션만 설정해두고 구해본 <code>accuracy_score</code>이다.</p>
<pre><code class="language-python">from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score

dt_clf = DecisionTreeClassifier(random_state=13, max_depth=4)
dt_clf.fit(X_train, y_train)
pred = dt_clf.predict(X_test)

accuracy_score(y_test, pred)</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/60acd06f-c990-45c5-8c8c-1869a4df2dc5/image.png" alt=""></p>
<br>

<h3 id="2-grid-search-cv를-적용한-decision-tree">2. Grid Search CV를 적용한 Decision Tree</h3>
<p>일단 <code>max_depth</code>만 여러 숫자로 돌려보자.</p>
<pre><code class="language-python">from sklearn.model_selection import GridSearchCV

params = {
    &#39;max_depth&#39; : [6,8,10,12,16,20,24]
}

grid_cv = GridSearchCV(dt_clf, param_grid=params, scoring = &#39;accuracy&#39;, 
                       cv = 5, return_train_score=True)

grid_cv.fit(X_train, y_train) # X_train에 대해 5등분한 교차검증 실행</code></pre>
<p><code>GridSearchCV</code>를 한 결과는 아래와 같은 함수를 호출하여 확인할 수 있다. </p>
<ul>
<li><code>grid_cv.best_scores_</code> : 검증 데이터에 대한 best score</li>
<li><code>grid_cv.best_params_</code> : 베스트 파라미터</li>
<li><code>grid_cv.cv_results_</code> : 보다 다양한 데이터 확인 가능</li>
</ul>
<p><code>grid_cv.cv_results_</code>를 통해 확인된 결과값들 중 원하는 결과값만 뽑아서 확인할 수도 있다.</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/70a64748-ee0a-411f-89b2-f90e1aadce3a/image.png" alt=""></p>
<p>위 결과에서 보여지는 test는 실제론 <code>X_train</code>에서 교차검증한 <code>validation set</code>에 대한 스코어이다. train 데이터에 대한 스코어는 높은데 validation 데이터에 대한 스코어는 그보다는 낮아 혹시 과적합이 걱정된다면 실제 <code>X_test</code>에 대한 스코어도 확인해보면 된다.</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/b8e72899-2932-4209-afe9-b808f96c7f1d/image.png" alt="">
<code>X_test</code>에 대한 스코어도 <code>max_depth</code>가 8일 때 가장 높은 것을 볼 수 있다. </p>
<p>따라서 우리의 베스트 모델은 아래와 같다.
<img src="https://velog.velcdn.com/images/castle_mi/post/660df093-29be-4577-8e4b-d8bccbbae0bb/image.png" alt=""></p>
<h3 id="3-grid-search-cv를-적용한-random-forest">3. Grid Search CV를 적용한 Random Forest</h3>
<p>그렇다면 <code>Decision Tree</code>를 여러 번 돌린 <code>Random Forest</code>도 한 번 돌려보자.</p>
<pre><code class="language-python">from sklearn.model_selection import GridSearchCV
from sklearn.ensemble import RandomForestClassifier

params = {
    &#39;max_depth&#39; : [6,8,10],
    &#39;n_estimators&#39; : [50,100,200], # Decision Tree 분류기 개수
    &#39;min_samples_leaf&#39; : [8,12], # 마지막 가지에 남아있는 최소 데이터 개수
    &#39;min_samples_split&#39; : [8,12] # 노드 당 최소 데이터 개수
}

rf_clf = RandomForestClassifier(random_state=13, n_jobs = -1)
# n_jobs = -1 : CPU를 다 써서 학습을 시켜라

grid_cv = GridSearchCV(rf_clf, param_grid=params, cv = 2, n_jobs= -1)
grid_cv.fit(X_train, y_train)</code></pre>
<pre><code class="language-python">cv_results_df = pd.DataFrame(grid_cv.cv_results_)

target_col = [&#39;rank_test_score&#39;, &#39;mean_test_score&#39;, &#39;param_n_estimators&#39;, &#39;param_max_depth&#39;]
cv_results_df[target_col].sort_values(&#39;rank_test_score&#39;).head()</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/8a31015f-e5b1-41ec-87ee-87d5c1dadb81/image.png" alt=""></p>
<p>위와 같이 표를 통해 확인할 수도 있고 <code>grid_cv.best_params_</code>를 통해 베스트 파라미터를 확인할 수도 있다. 
<img src="https://velog.velcdn.com/images/castle_mi/post/67b7d2d7-2199-4ddf-862f-6d5f668f34b8/image.png" alt=""></p>
<pre><code class="language-python">rf_clf_best = grid_cv.best_estimator_
rf_clf_best.fit(X_train, y_train)
pred1 = rf_clf_best.predict(X_test)

accuracy_score(y_test, pred1)</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/d39c5969-88d6-4c09-b351-0ea488d163ea/image.png" alt=""></p>
<p><code>Random Forest</code>에 <code>GridSearchCV</code>까지 했더니 모델 성능이 많이 향상된 것을 볼 수 있다!!</p>
<h3 id="4-중요-feature들만-골라-예측한-random-forest">4. 중요 feature들만 골라 예측한 Random Forest</h3>
<p>하지만 파라미터를 많이 넣을수록 feature들이 많아질수록 <code>GridSearch</code>하는데에, 그리고 학습시키는데에 많은 시간이 소요된다. </p>
<p>계산량을 줄이기 위해 중요 feature들만 학습시킬 필요가 있다.</p>
<p>앞서 회귀 문제에서는 <code>feature importance</code>를 계수값을 통해 확인했었다.</p>
<p>하지만 <code>tree</code>모델에서는 <code>feature_importances_</code>라는 것이 있다.</p>
<pre><code class="language-python">best_cols_values = rf_clf_best.feature_importances_
best_cols = pd.Series(best_cols_values, index = X_train.columns)
top20_cols = best_cols.sort_values(ascending = False)[:20]
top20_cols</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/67966433-36f3-40eb-a0ee-75b624c0943a/image.png" alt=""></p>
<p>위와 같이 모델링할 때 중요하게 사용된 feature들을 한 눈에 확인할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/7c1cedd3-c8c5-4a58-9a68-0aaf7d7d9f4c/image.png" alt="">
이를 시각화하면 더 한 눈에 알아볼 수 있는데 이 중요 feature들만 가지고 모델링을 할 수도 있다.</p>
<pre><code class="language-python"># 20개 특성만 가지고 모델링 다시

X_train_re = X_train[top20_cols.index]
X_test_re = X_test[top20_cols.index]

rf_clf_best_re = grid_cv.best_estimator_
rf_clf_best_re.fit(X_train_re, y_train)

pred1_re = rf_clf_best_re.predict(X_test_re)
accuracy_score(y_test, pred1_re)</code></pre>
<blockquote>
<p>💻 출력</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/83524eda-bacb-4783-b63f-28018af37f72/image.png" alt=""></p>
<p>물론 모든 feature를 사용한 것과 비교하면 <code>accuracy</code>는 많이 떨어졌다. 하지만 학습 시간은 확실히 줄어들었다.</p>
<p>이와 같이 학습량이 많아도 모든 feature를 학습시킬건지, 일부 중요 feature만 가지고 학습시킬건지 또한 데이터 분석가가 결정해야할 사항 중 하나가 될 것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[캐글 필사] 타이타닉 데이터 EDA]]></title>
            <link>https://velog.io/@castle_mi/%EC%BA%90%EA%B8%80-%ED%95%84%EC%82%AC-%ED%83%80%EC%9D%B4%ED%83%80%EB%8B%89-%EB%8D%B0%EC%9D%B4%ED%84%B0-EDA</link>
            <guid>https://velog.io/@castle_mi/%EC%BA%90%EA%B8%80-%ED%95%84%EC%82%AC-%ED%83%80%EC%9D%B4%ED%83%80%EB%8B%89-%EB%8D%B0%EC%9D%B4%ED%84%B0-EDA</guid>
            <pubDate>Sun, 24 Sep 2023 06:16:16 GMT</pubDate>
            <description><![CDATA[<p>✍🏻 22일 팀스터니 내용 정리.</p>
<p>👀 공부한 실습 코드는 아래 깃허브에 올려두었습니다.</p>
<p><a href="https://github.com/castlemi99/ZeroBaseDataSchool/tree/main/%EC%BA%90%EA%B8%80%20%ED%95%84%EC%82%AC%20%EC%8A%A4%ED%84%B0%EB%94%94">https://github.com/castlemi99/ZeroBaseDataSchool/tree/main/%EC%BA%90%EA%B8%80%20%ED%95%84%EC%82%AC%20%EC%8A%A4%ED%84%B0%EB%94%94</a></p>
<br>

<p>자소서/면접 스터디가 지나고 캐글 필사 스터디로 넘어왔다 :)</p>
<p>그동안 미뤄왔기만 했던 자기소개서 작성을 지난 스터디를 계기로 건드려보기만 한 것 자체로 뿌듯하다 ㅎㅎ</p>
<p>그래도 아직 빈약한 부분이 많아서 다음주에 있을 자기소개서 특강을 잘 듣고 살을 덧붙여봐야겠다.</p>
<p><br><br></p>
<p>캐글 필사 스터디로 넘어왔다. 캐글 문제는 대학 생활 때 많이 건드려보긴 했는데 어딘가에 작성이라도 해둘걸😭 너무 아쉬웠다.</p>
<p>머신러닝의 기본 예제라고 할 수 있는 <a href="https://www.kaggle.com/c/titanic">타이타닉</a>을 첫 번째 캐글 필사로 정하고 강의 진도에 맞게 EDA 부분만 필사해보기로 했다.</p>
<p>캐글 API를 사용해 데이터를 다운받는 것은 <a href="https://velog.io/@skyepodium/Kaggle-API-%EC%82%AC%EC%9A%A9%EB%B2%95">👀 이 분 사이트</a>를 참고해서 다운받아따😀</p>
<br>

<p>필사하면서 몇 가지 배운점을 정리하자면</p>
<h2 id="결측치를-확인하기-쉬운-라이브러리--mano">결측치를 확인하기 쉬운 라이브러리 : mano</h2>
<p>결측치를 확인하는 과정에서 <code>mano</code>라는 라이브러리를 배웠다.</p>
<p><a href="https://github.com/ResidentMario/missingno">https://github.com/ResidentMario/missingno</a></p>
<p>단순히 수치적으로 확인하는 <code>.isnull().sum()</code>을 많이 사용했었는데
<code>mano</code>는 결측치를 시각화해서 보여준다.</p>
<p><code>msno.matrix(df = 데이터프레임, figsize = , color = )</code></p>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/e0929e4b-5ed4-490b-a7e4-3b5704798da5/image.png" alt=""></th>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/7dd71ccd-69aa-407c-953f-4b45beb45833/image.png" alt=""></th>
</tr>
</thead>
</table>
<p>이렇게 보고자하는 데이터프레임과 색상값을 넣어주면 결측치가 있는 곳이 비어져 출력된다.</p>
<p><code>bar</code>는 컬럼별 결측치의 누적값을 볼 수 있어 <code>Age</code>와 <code>Cabin</code> 컬럼에 결측치가 있고 <code>Cabin</code>은 결측치가 매우 많은 것을 볼 수 있다.</p>
<br>

<h2 id="factorplot-은-catplot으로-변경됨">Factorplot 은 Catplot으로 변경됨</h2>
<p>많은 분들이 <code>factorplot</code>을 많이 사용하셨는데 23년도인 지금 그 코드를 사용하면 차트가 출력되지 않는다.</p>
<p>📌 <code>factorplot</code> 공식 사이트
<a href="https://www.geeksforgeeks.org/python-seaborn-factorplot-method/">https://www.geeksforgeeks.org/python-seaborn-factorplot-method/</a></p>
<p>이렇게 공식사이트도 있어서 아직 살아있는줄(?) 알았는데 
<a href="https://seaborn.pydata.org/archive/0.11/whatsnew.html#:~:text=The%20factorplot%20function%20has%20been%20renamed%20to%20catplot()"><img src="https://velog.velcdn.com/images/castle_mi/post/843b4293-e58c-46b2-9fa0-81880469b380/image.png" alt=""></a></p>
<p>API가 <code>catplot</code>으로 바뀌었다는 것을 알 수 있었다.</p>
<p>📌 <code>catplot</code> 공식 사이트
<a href="https://seaborn.pydata.org/archive/0.11/generated/seaborn.catplot.html#seaborn.catplot">https://seaborn.pydata.org/archive/0.11/generated/seaborn.catplot.html#seaborn.catplot</a></p>
<p><img src="https://velog.velcdn.com/images/castle_mi/post/bbd5b3ed-8eb9-4bfb-b9fc-2e4c7aaa12c9/image.png" alt=""></p>
<p><code>catplot</code> 에 <code>kind</code>옵션을 주어 해당 차트를 그려도 되고 아니면 직접 <code>stripplot</code> 과 같이 직접 호출해 차트를 그려도 된다.</p>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/97f9a94f-6c12-42e1-a9ac-f02b97db94b7/image.png" alt=""></th>
<th><img src="https://velog.velcdn.com/images/castle_mi/post/376604fc-7ece-48b9-8cca-be57c68f3f05/image.png" alt=""></th>
</tr>
</thead>
</table>
<p>그런데 한 가지 주의해야할 점이 있다. <code>figure</code>과 <code>axes</code>를 생성하고 <code>catplot</code>을 통해 차트를 생성하게 되면 <code>catplot</code> 호출 시 새로운 <code>figure</code>과 <code>axes</code>를 생성하게 되므로 원했던 목적대로 배경에 차트가 구분되어 출력되는 것이 아닌 두 개의 차트가 별도로 표시되는 것을 볼 수 있다. </p>
<p>따라서 이를 해결하기 위해선 <code>catplot</code>대신 그리려는 차트 함수를 직접 호출해야한다. </p>
<p>📌 차트가 별도로 출력되는 코드</p>
<pre><code class="language-python">f,ax=plt.subplots(1,2,figsize=(18,6))

# 첫번째 차트 : 가족수 별 생존율
sns.catplot(x = &#39;FamilySize&#39;, y = &#39;Survived&#39;,data=df_train, kind = &#39;point&#39;, ax=ax[0])
ax[0].set_title(&#39;FamilySize vs Survived&#39;)

# 두번째 차트 : 혼자 탑승했는가의 유무에 따른 생존율
sns.catplot(x = &#39;Alone&#39;, y = &#39;Survived&#39;,data=df_train, kind = &#39;point&#39;, ax=ax[1])
ax[1].set_title(&#39;Alone vs Survived&#39;)

plt.show()</code></pre>
<br>

<p>📌 우리의 목적대로 출력되는 코드
<img src="https://velog.velcdn.com/images/castle_mi/post/41c5fbe7-a882-467e-b0e5-bdb84b690ee5/image.png" alt=""></p>
<p><br><br></p>
<p><code>Name</code> 컬럼에서 신분을 추출하고, <code>Parch(부모, 자녀)</code>와 <code>SibSp(형제 자매)</code> 컬럼을 통해 가족수와 혼자 탑승했는가에 대한 여부 등 여러 컬럼을 생성하여 타겟 데이터에 영향을 끼치는지에 대해 알아보는 것을 통해 과연 내가 혼자 데이터를 분석을 할 때 이정도까지 파악해낼 수 있을까 고민이 되었다. </p>
<p>대학교 과제를 할 때에도 이 EDA 과정에서 시간을 제일 많이 보내기도 했고.. 지금도 그렇고..ㅎㅎ </p>
<p>솔직히 당연히 오랜 시간을 거칠 수 밖에 없는 과정이라고 생각한다. 하지만 우연히 이것저것 해보다가 발견하는 것이 아닌 내가 그 인사이트를 먼저 발견할줄 아는 사람이 되기 위해 생각을 넓고 다양하게 할 수 있어야겠다는 생각이 들었다.👊🏻👊🏻</p>
<br>


]]></description>
        </item>
    </channel>
</rss>