<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>ecosso 님의 블로그</title>
    <link>https://ecosso.tistory.com/</link>
    <description>ecosso 님의 블로그 입니다.</description>
    <language>ko</language>
    <pubDate>Sun, 23 Aug 2026 12:50:57 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>ecosso</managingEditor>
    <item>
      <title>[골목길 탐지하기]_종로구 정사영상 기준 실험 (2/2)</title>
      <link>https://ecosso.tistory.com/151</link>
      <description>&lt;p data-end=&quot;353&quot; data-start=&quot;265&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;최근 진행 중인 프로젝트의 일부로, 공개 도로 데이터를 보완할 수 있는 방법을 고민하다가 간단한 토이 프로젝트를 진행하고 있었다.&lt;/p&gt;
&lt;p data-end=&quot;353&quot; data-start=&quot;265&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ecosso.tistory.com/150&quot;&gt;[골목길 탐지하기]_종로구 정사영상 기준 실험 (1/2)&lt;/a&gt;&lt;/p&gt;
&lt;p data-end=&quot;353&quot; data-start=&quot;265&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-end=&quot;353&quot; data-start=&quot;265&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이전 실험에서는 기존 공개 도로데이터를 학습 라벨로 활용하여 정사영상에서 도로를 탐지하고자 했다. 그러나 실험을 반복하면서 모델 구조를 변경하는 것만으로 해결하기 어려운 문제가 있다는 점을 확인했다.&lt;/p&gt;
&lt;p data-end=&quot;863&quot; data-start=&quot;753&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;863&quot; data-start=&quot;753&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;특히 기존 도로데이터에는 실제 보행에 이용되는 일부 골목길이 포함되어 있지 않았고, 도로 중심선을 일정한 폭으로 확장하여 만든 라벨 역시 실제 영상에서 보이는 도로 경계와 정확하게 일치하지 않았다.&lt;/p&gt;
&lt;p data-end=&quot;933&quot; data-start=&quot;865&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;933&quot; data-start=&quot;865&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;결국 모델에게 더 복잡한 구조를 적용하기 전에 &lt;b&gt;학습에 사용하는 정답 데이터부터 다시 확인할 필요가 있다&lt;/b&gt;고 판단했다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-end=&quot;565&quot; data-start=&quot;521&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;[직접 제작(디지타이징)한 라벨 사용]&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;1011&quot; data-start=&quot;960&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이전 과정에서는 표준도로링크와 토지피복도로부터 제작한 중심선 + 2m 버퍼 등 여러 데이터를 이용하여 학습 라벨을 구성해 보았다.&lt;/p&gt;
&lt;p data-end=&quot;1135&quot; data-start=&quot;1013&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1135&quot; data-start=&quot;1013&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;하지만 위에서 기술한 것처럼 골목길을 탐지하는 것이 목적임에도 정작 학습에 사용하는 도로데이터에 일부 골목길이 존재하지 않는다는 문제가 있었다. 또한 영상의 공간해상도가 충분하지 않은 경우 좁은 도로와 주변 객체의 경계를 구분하기 어려웠다.&lt;/p&gt;
&lt;p data-end=&quot;1202&quot; data-start=&quot;1137&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1202&quot; data-start=&quot;1137&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이에 일부 지역의 도로를 직접 디지타이징하여 학습 라벨을 다시 구축하였다. 즉, 원하는 수준까지의 도로 및 도로폭까지 모두 제작을 진행하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;615&quot; data-origin-height=&quot;861&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6R2Xh/dJMcagNpK5L/OKgwRTVr8nKXmmK0khVWM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6R2Xh/dJMcagNpK5L/OKgwRTVr8nKXmmK0khVWM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6R2Xh/dJMcagNpK5L/OKgwRTVr8nKXmmK0khVWM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6R2Xh%2FdJMcagNpK5L%2FOKgwRTVr8nKXmmK0khVWM1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;615&quot; height=&quot;861&quot; data-origin-width=&quot;615&quot; data-origin-height=&quot;861&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;1293&quot; data-start=&quot;1204&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1293&quot; data-start=&quot;1204&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1293&quot; data-start=&quot;1204&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 과정에서 &lt;b&gt;모델 구조뿐만 아니라 입력 영상의 품질과 학습 라벨의 정확도 역시 탐지 결과에 중요한 영향을 미칠 수 있다는 점&lt;/b&gt;을 확인할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-end=&quot;640&quot; data-start=&quot;610&quot; data-section-id=&quot;5ycfa1&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[하나의 모델이 아니라 세 가지 모델을 비교한 이유]&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;640&quot; data-start=&quot;610&quot; data-section-id=&quot;5ycfa1&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;747&quot; data-start=&quot;642&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;라벨을 보완한 이후에는 하나의 모델로만 학습&amp;middot;추론을 진행하지 않고, 서로 다른 구조를 가진 세 가지 Semantic Segmentation 모델을 동일한 데이터 조건에서 비교하기로 하였다.&lt;/p&gt;
&lt;p data-end=&quot;803&quot; data-start=&quot;749&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;803&quot; data-start=&quot;749&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;세 모델을 비교하게 된 계기는 &lt;b&gt;초기 U-Net 추론 결과에서 확인한 도로의 단절 문제&lt;/b&gt;였다.&lt;/p&gt;
&lt;p data-end=&quot;991&quot; data-start=&quot;805&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;991&quot; data-start=&quot;805&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;항공정사영상에서는 도로 위를 덮고 있는 수목이나 건물에 의해 생긴 그림자 때문에 실제로는 하나의 도로임에도 일부 구간의 형태와 색상이 크게 달라지는 경우가 있었다. 특히 수목이 우거진 구간에서는 도로가 가려지면서 모델의 예측 결과가 중간에서 끊기고, 가려진 구간 이후부터 다시 새로운 도로가 시작되는 것처럼 탐지되는 사례가 나타났다.&lt;/p&gt;
&lt;p data-end=&quot;991&quot; data-start=&quot;805&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1113&quot; data-start=&quot;993&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;실제 도로는 연속적인 구조를 가지고 있지만, 수목이나 그림자 등으로 도로의 시각적 특징이 크게 달라지는 구간에서는 &lt;b&gt;모델이 가려진 구간의 앞뒤를 하나의 연속된 도로로 충분히 복원하지 못하는 사례가 나타났다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;1132&quot; data-start=&quot;1115&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1132&quot; data-start=&quot;1115&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;여기서 한 가지 질문이 생겼다. :&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1211&quot; data-start=&quot;1136&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;i&gt;도로처럼 길고 연속적인 구조를 가진 객체를 탐지할 때, 더 넓은 주변 영역과의 관계를 고려하는 모델을 사용하면 결과가 달라질까?&lt;/i&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1296&quot; data-start=&quot;1213&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 질문을 바탕으로 기존에 사용하던 U-Net을 기준 모델로 두고, 보다 넓은 범위의 공간적&amp;middot;문맥적 정보를 활용할 수 있는 구조를 추가로 비교하였다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1862&quot; data-start=&quot;1298&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1443&quot; data-start=&quot;1298&quot; data-section-id=&quot;1mcnbuk&quot;&gt;&lt;b&gt;U-Net&lt;/b&gt;&lt;br /&gt;&amp;nbsp;Encoder-Decoder 구조와 Skip Connection을 사용하는 대표적인 Semantic Segmentation 모델이다. 기존 실험에서 사용했던 모델이므로 비교를 위한 기준 모델(Baseline)로 설정하였다.&lt;/li&gt;
&lt;li data-end=&quot;1659&quot; data-start=&quot;1445&quot; data-section-id=&quot;xzzacl&quot;&gt;&lt;b&gt;D-LinkNet&lt;/b&gt;&lt;br /&gt;LinkNet 계열의 Encoder-Decoder 구조에 Dilated Convolution을 활용하여, 특징맵의 해상도를 추가로 크게 낮추지 않으면서 넓은 수용영역(Receptive Field)을 확보할 수 있다. 도로처럼 가늘고 길게 이어지는 객체의 segmentation에 활용된 모델이라는 점에서, 주변 문맥을 보다 넓게 고려했을 때 단절된 도로 탐지 결과가 달라지는지 확인하기 위해 선택하였다.&lt;/li&gt;
&lt;li data-end=&quot;1862&quot; data-start=&quot;1661&quot; data-section-id=&quot;cmo4og&quot;&gt;&lt;b&gt;SegFormer-B0&lt;/b&gt;&lt;br /&gt;&amp;nbsp;CNN 중심의 앞선 두 모델과 달리 Transformer 기반 Encoder를 사용하여 &lt;b&gt;CNN과 다른 방식으로 공간적 특징과 문맥 정보를 학습하는 모델이다.&lt;/b&gt; CNN 기반 모델과 다른 방식으로 주변 정보를 활용했을 때 도로 탐지 결과에 어떤 차이가 나타나는지 비교하기 위해 선택하였다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rwRdB/dJMcabk10vl/vc0Na6rrKKwI8oZBG99MBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rwRdB/dJMcabk10vl/vc0Na6rrKKwI8oZBG99MBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rwRdB/dJMcabk10vl/vc0Na6rrKKwI8oZBG99MBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrwRdB%2FdJMcabk10vl%2Fvc0Na6rrKKwI8oZBG99MBk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;1924&quot; data-start=&quot;1864&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;세 모델을 동일한 학습 데이터와 조건에서 학습한 뒤, 모델별로 설정한 임계값을 적용하여 종로구 내 2개 격자분에 해당하는 정사영상에 추론하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이번 단계에서는 단순히 특정 평가지표가 가장 높은 모델을 선정하기보다, &lt;b&gt;&amp;nbsp;수목이나 그림자 등에 의해 도로의 시각적 특징이 부분적으로 달라지는 상황에서, 서로 다른 방식으로 주변 문맥을 학습하는 모델들이 연속적인 도로 구조를 어떻게 탐지하는지 비교하는 것&lt;/b&gt;에 초점을 두었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 30.4651%;&quot;&gt;&lt;b&gt;Model&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 21.8605%;&quot;&gt;&lt;b&gt; Threshold &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 24.4186%;&quot;&gt;&lt;b&gt;도로 폴리곤 수&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 23.1395%;&quot;&gt;&lt;b&gt;중심선 수&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 30.4651%;&quot;&gt;&lt;b&gt;U-Net&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 21.8605%;&quot;&gt;&lt;b&gt;0.700&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 24.4186%;&quot;&gt;&lt;b&gt;10,431&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 23.1395%;&quot;&gt;&lt;b&gt;36,114&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 30.4651%;&quot;&gt;&lt;b&gt;D-LinkNet&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 21.8605%;&quot;&gt;&lt;b&gt;0.625&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 24.4186%;&quot;&gt;&lt;b&gt;16,652&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 23.1395%;&quot;&gt;&lt;b&gt;65,815&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 30.4651%;&quot;&gt;&lt;b&gt;SegFormer-B0&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 21.8605%;&quot;&gt;&lt;b&gt;0.625&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 24.4186%;&quot;&gt;&lt;b&gt;47,294&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 23.1395%;&quot;&gt;&lt;b&gt;89,404&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3507&quot; data-origin-height=&quot;2480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwCK8Z/dJMcafHJFFj/fKSgTtIFrEF4ltGkpJw7MK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwCK8Z/dJMcafHJFFj/fKSgTtIFrEF4ltGkpJw7MK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwCK8Z/dJMcafHJFFj/fKSgTtIFrEF4ltGkpJw7MK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwCK8Z%2FdJMcafHJFFj%2FfKSgTtIFrEF4ltGkpJw7MK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3507&quot; height=&quot;2480&quot; data-origin-width=&quot;3507&quot; data-origin-height=&quot;2480&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;▲ U-Net 적용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3507&quot; data-origin-height=&quot;2480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QPpKI/dJMcaft60Fo/FmLVxKksKr4nPVbsCRIRF0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QPpKI/dJMcaft60Fo/FmLVxKksKr4nPVbsCRIRF0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QPpKI/dJMcaft60Fo/FmLVxKksKr4nPVbsCRIRF0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQPpKI%2FdJMcaft60Fo%2FFmLVxKksKr4nPVbsCRIRF0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3507&quot; height=&quot;2480&quot; data-origin-width=&quot;3507&quot; data-origin-height=&quot;2480&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;▲ D-Linknet 적용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3507&quot; data-origin-height=&quot;2480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oyzFU/dJMcaa7vhDk/a7KbHg6ZqKL8I1FiuoAYf1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oyzFU/dJMcaa7vhDk/a7KbHg6ZqKL8I1FiuoAYf1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oyzFU/dJMcaa7vhDk/a7KbHg6ZqKL8I1FiuoAYf1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoyzFU%2FdJMcaa7vhDk%2Fa7KbHg6ZqKL8I1FiuoAYf1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3507&quot; height=&quot;2480&quot; data-origin-width=&quot;3507&quot; data-origin-height=&quot;2480&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;▲ SegFormer-B0 적용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;종로구 내 2개 격자의 항공영상에 적용한 결과, 모델에 따라 추출되는 도로 영역의 양과 형태에 상당한 차이가 나타났다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;U-Net은 세 모델 가운데 가장 적은 수의 도로 폴리곤과 중심선을 생성하여 상대적으로 보수적인 탐지 양상을 보였다. D-LinkNet은 U-Net보다 많은 도로 영역과 중심선을 추출했으며, SegFormer-B0은 47,294개의 폴리곤과 89,404개의 중심선을 생성하여 가장 많은 영역을 도로로 탐지하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;다만 생성된 폴리곤이나 중심선의 개수만으로 탐지 성능의 우열을 판단할 수는 없다는 것을 알 수 있었다. 기존 도로데이터에서 누락된 협소도로를 추가로 탐지하여 개수가 증가할 수도 있지만, 반대로 비도로 영역을 잘못 탐지하거나 하나의 도로가 여러 조각으로 분절되면서 개수가 증가할 수도 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;실제 결과를 함께 확인했을 때 SegFormer-B0에서는 다른 두 모델보다 탐지 영역이 크게 증가하는 한편, 비도로 영역까지 도로로 판단하거나 결과가 잘게 분절되는 사례도 상대적으로 많이 관찰되었다. 반면 U-Net은 비교적 보수적인 탐지 결과를 보였으며, D-LinkNet은 두 모델의 중간적인 양상을 나타냈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 결과를 통해 &lt;b&gt;도로 탐지 모델을 평가할 때에는 탐지 영역의 양뿐만 아니라 실제 공간에서 나타나는 도로의 연속성, 단절, 누락, 오탐 및 불필요한 가지와 같은 공간적 특성도 함께 확인할 필요가 있다&lt;/b&gt;고 판단하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;특히 이번 프로젝트의 최종 목적은 탐지된 도로를 중심선으로 변환하여 보행 네트워크 분석에 활용하는 것이므로, 최종 모델 선정 역시 단순한 탐지량보다는 &lt;b&gt;실제 도로망과의 정합성 및 네트워크 연결성을 추가로 검증한 뒤 결정할 필요가 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;따라서 현재 단계에서는 특정 모델을 최종 모델로 확정하기보다, &lt;b&gt;서로 다른 구조의 모델이 동일한 영상에서도 상당히 다른 공간적 결과를 생성한다는 점을 확인한 비교 실험&lt;/b&gt;으로 정리하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-end=&quot;324&quot; data-start=&quot;297&quot; data-section-id=&quot;l0js04&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[반복 실험을 위한 CUDA 환경 구축]&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;324&quot; data-start=&quot;297&quot; data-section-id=&quot;l0js04&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;356&quot; data-start=&quot;326&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이번 실험에서 또 하나의 문제는 &lt;b&gt;연산시간&lt;/b&gt;이었다.&lt;/p&gt;
&lt;p data-end=&quot;469&quot; data-start=&quot;358&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;초기에는 별도의 GPU가 없는 학원 컴퓨터에서 모델을 학습했는데, 한 번의 학습에 약 &lt;b&gt;7~9시간&lt;/b&gt;이 소요될 것으로 예상되었다. 학습이 진행되는 동안에는 컴퓨터를 다른 용도로 활용하기도 어려웠다.&lt;/p&gt;
&lt;p data-end=&quot;671&quot; data-start=&quot;471&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;671&quot; data-start=&quot;471&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;처음에는 이러한 제약을 해결하기 위해 여러 컴퓨터에 지역별 학습을 분산하고, 각 지역에서 학습한 모델을 활용하는 방법도 검토하였다. 그러나 사용할 수 있는 컴퓨터가 제한적이었고 일부 장비는 정상적으로 활용하기 어려웠다. 무엇보다 한 번의 실험에 반나절 가까운 시간이 필요한 상황에서는 여러 조건을 바꾸어가며 결과를 반복적으로 확인하는 것 자체가 쉽지 않았다.&lt;/p&gt;
&lt;p data-end=&quot;739&quot; data-start=&quot;673&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;739&quot; data-start=&quot;673&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Semantic Segmentation 실험에서는 한 번 모델을 학습하는 것으로 끝나는 것이 아니라 결과를 확인한 뒤,&lt;/p&gt;
&lt;p data-end=&quot;784&quot; data-start=&quot;741&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;784&quot; data-start=&quot;741&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;학습 &amp;rarr; 결과 확인 &amp;rarr; 문제 추정 &amp;rarr; 데이터&amp;middot;라벨&amp;middot;모델 조정 &amp;rarr; 재학습&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;798&quot; data-start=&quot;786&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;798&quot; data-start=&quot;786&quot; data-ke-size=&quot;size16&quot;&gt;위의 과정을 반복해야 했다.&lt;/p&gt;
&lt;p data-end=&quot;1001&quot; data-start=&quot;800&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1001&quot; data-start=&quot;800&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;예를 들어 결과가 좋지 않다면 학습 라벨의 품질이 원인인지, 입력 영상의 문제인지, 모델 구조나 학습 조건의 문제인지를 확인하기 위해 다시 실험해야 한다. 그러나 한 번의 결과를 확인하는 데 7~9시간이 걸린다면 몇 가지 가설을 검증하는 데만 며칠이 필요하고, 이후 서울시 여러 지역으로 실험을 확장하는 것은 현실적으로 더 큰 시간적 제약이 될 수밖에 없었다.&lt;/p&gt;
&lt;p data-end=&quot;1102&quot; data-start=&quot;1003&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1102&quot; data-start=&quot;1003&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;따라서 단순히 모델의 성능을 높이는 것뿐만 아니라 &lt;b&gt;실험 한 번에 필요한 시간을 줄여 반복적인 검증이 가능한 환경을 만드는 것 자체가 먼저 해결해야 할 문제&lt;/b&gt;라고 판단하였다.&lt;/p&gt;
&lt;p data-end=&quot;1252&quot; data-start=&quot;1104&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1252&quot; data-start=&quot;1104&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;관련 논문과 딥러닝 학습 사례를 찾아보는 과정에서 NVIDIA GPU의 병렬연산을 활용하는 &lt;b&gt;CUDA&lt;/b&gt;에 대해 알게 되었고, 개인 컴퓨터에 NVIDIA GPU가 탑재되어 있다는 점을 활용하여 &lt;b&gt;PyTorch&amp;middot;CUDA 기반 GPU 학습환경을 직접 구축&lt;/b&gt;하였다.&lt;/p&gt;
&lt;p data-end=&quot;1382&quot; data-start=&quot;1254&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1382&quot; data-start=&quot;1254&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이를 통해 대용량 정사영상과 여러 모델을 반복적으로 학습&amp;middot;추론할 수 있는 환경을 마련하였고, 동일한 조건에서 U-Net, D-LinkNet, SegFormer-B0의 결과를 비교하는 실험도 보다 현실적으로 수행할 수 있게 되었다.&lt;/p&gt;
&lt;p data-end=&quot;1492&quot; data-start=&quot;1384&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1492&quot; data-start=&quot;1384&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이번 경험에서 CUDA 자체를 사용하는 것이 목적은 아니었다. 오히려 의미가 있었던 부분은 &lt;b&gt;한정된 인프라와 시간 안에서 더 많은 가설을 검증하기 위해 실험환경 자체를 개선했다는 점&lt;/b&gt;이었다.&lt;/p&gt;
&lt;p data-end=&quot;1651&quot; data-start=&quot;1494&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1651&quot; data-start=&quot;1494&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;모델 개발에서는 정확도뿐만 아니라 데이터 규모, 연산시간, 메모리, 사용 가능한 하드웨어와 같은 현실적인 제약 역시 실험 설계의 일부이며, &lt;b&gt;빠르게 결과를 확인하고 다음 실험으로 이어갈 수 있는 환경을 만드는 것 또한 모델 개선 과정의 중요한 요소&lt;/b&gt;라는 점을 경험할 수 있었다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-end=&quot;3530&quot; data-start=&quot;3511&quot; data-section-id=&quot;1us6emb&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;3530&quot; data-start=&quot;3511&quot; data-section-id=&quot;1us6emb&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[이번 실험에서 확인한 것]&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;3530&quot; data-start=&quot;3511&quot; data-section-id=&quot;1us6emb&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;511&quot; data-start=&quot;445&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이번 프로젝트를 진행하면서 가장 크게 느낀 점은 &lt;b&gt;모델의 성능은 모델 구조 하나만으로 결정되지 않는다는 것&lt;/b&gt;이었다.&lt;/p&gt;
&lt;p data-end=&quot;676&quot; data-start=&quot;513&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;초기 실험에서는 기대했던 만큼 협소도로를 탐지하지 못했지만, 원인을 확인하는 과정에서 모델뿐만 아니라 &lt;b&gt;영상의 공간해상도와 학습 라벨의 품질이 결과에 큰 영향을 미친다는 점&lt;/b&gt;을 확인하였다. 이에 더 높은 해상도의 영상을 확보하고 일부 지역의 라벨을 직접 구축하여 학습 데이터를 개선하였다.&lt;/p&gt;
&lt;p data-end=&quot;914&quot; data-start=&quot;678&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;914&quot; data-start=&quot;678&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이후에는 수목이나 그림자 등에 의해 실제로는 연속된 도로가 중간에서 끊겨 탐지되는 문제에 주목하였다. 이를 바탕으로 주변 문맥을 서로 다른 방식으로 학습하는 모델에서는 결과가 달라지는지 확인하기 위해 U-Net, D-LinkNet, SegFormer-B0을 동일한 조건에서 비교하였다. &lt;b&gt;종로구 영상에 적용한 결과 모델별로 탐지 영역의 양과 분절 양상이 상당히 다르게 나타났으며, 이를 통해 탐지량뿐만 아니라 실제 도로의 연속성, 누락, 오탐과 같은 공간적 특성을 함께 검토할 필요가 있다고 판단하였다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;1081&quot; data-start=&quot;916&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1081&quot; data-start=&quot;916&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;반복적인 학습과 비교 과정에서는 연산시간 자체가 실험의 병목이 되기도 했다. 이를 해결하기 위해 PyTorch&amp;middot;CUDA 기반 GPU 환경을 구축하면서, &lt;b&gt;분석에서는 모델과 데이터뿐만 아니라 제한된 시간과 연산자원 안에서 반복적인 실험이 가능한 환경을 만드는 것도 중요하다&lt;/b&gt;는 점을 경험하였다.&lt;/p&gt;
&lt;p data-end=&quot;1268&quot; data-start=&quot;1083&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1268&quot; data-start=&quot;1083&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;물론 아직 한계는 남아 있다. 건물이나 그림자 등 도로와 유사한 영역을 잘못 탐지하거나 일부 협소도로를 놓치는 사례가 있으며, 현재 종로구를 중심으로 한 실험 결과만으로 특정 모델이 다른 지역에서도 가장 우수하다고 일반화하기는 어렵다. 따라서 향후에는 다른 지역에서도 모델의 성능과 탐지 양상이 유지되는지 추가적인 검증이 필요하다.&lt;/p&gt;
&lt;p data-end=&quot;1307&quot; data-start=&quot;1270&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1307&quot; data-start=&quot;1270&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이번 프로젝트에서는 다음의 과정을 하나의 흐름으로 직접 수행하였다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1482&quot; data-start=&quot;1309&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;4586&quot; data-start=&quot;4562&quot; data-section-id=&quot;t5uq4i&quot;&gt;공간데이터와 영상데이터의 전처리 및 정합&lt;/li&gt;
&lt;li data-end=&quot;4604&quot; data-start=&quot;4587&quot; data-section-id=&quot;tc8tyt&quot;&gt;학습 라벨 구축과 품질 검토&lt;/li&gt;
&lt;li data-end=&quot;4644&quot; data-start=&quot;4605&quot; data-section-id=&quot;1wqwa87&quot;&gt;서로 다른 Semantic Segmentation 모델의 학습&amp;middot;비교&lt;/li&gt;
&lt;li data-end=&quot;4668&quot; data-start=&quot;4645&quot; data-section-id=&quot;1fq7qd8&quot;&gt;GPU 기반 반복 학습&amp;middot;추론 환경 구축&lt;/li&gt;
&lt;li data-end=&quot;4697&quot; data-start=&quot;4669&quot; data-section-id=&quot;1tbet15&quot;&gt;종로구 내 영상에 대한 모델별 추론 결과 비교&lt;/li&gt;
&lt;li data-end=&quot;4743&quot; data-start=&quot;4698&quot; data-section-id=&quot;hloyn9&quot;&gt;예측 Mask의 Polygon&amp;middot;Centerline 변환 및 후속 공간분석 연결&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1615&quot; data-start=&quot;1484&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이번 프로젝트의 가장 큰 수확은 단순히 &amp;lsquo;도로를 얼마나 잘 찾았는가&amp;rsquo;라는 최종 성능보다, 결과가 기대와 다를 때 하나의 원인으로 단정하지 않고 데이터, 라벨, 모델, 학습환경을 차례로 점검하며 다음 실험을 설계해 본 경험이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개인 프로젝트/골목길 탐지하기</category>
      <author>ecosso</author>
      <guid isPermaLink="true">https://ecosso.tistory.com/151</guid>
      <comments>https://ecosso.tistory.com/151#entry151comment</comments>
      <pubDate>Thu, 6 Aug 2026 17:31:16 +0900</pubDate>
    </item>
    <item>
      <title>[골목길 탐지하기]_종로구 정사영상 기준 실험 (1/2)</title>
      <link>https://ecosso.tistory.com/150</link>
      <description>&lt;p data-end=&quot;32&quot; data-start=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;공공데이터로 제공받을 수 있는 도로가 실제 이동 가능한 모든 길을 포함하는 것은 아니다. 도로를 어느 기준까지 포함하여 작도할지는 각 자료가 제작된 목적에 따라 달라질 수도 있기 때문이다.&lt;/p&gt;
&lt;p data-end=&quot;32&quot; data-start=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;특히 주거지 내부의 골목길과 생활도로는 기존 도로 자료에서 누락되는 경우가 있으며, 이로 인해 보행 경로 분석에서는 실제보다 긴 우회 경로가 계산될 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;32&quot; data-start=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;32&quot; data-start=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;따라서 국토정보플랫폼에서 제공하는 정사영상을, 데이터는 표준노드링크의 링크 데이터를 사용하여 서울특별시 내 보행 가능한 골목길을 탐지하는 과제를 진행해보기로 했다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-end=&quot;32&quot; data-start=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;32&quot; data-start=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;우선 분석 전에 서울특별시 전체의 정사영상은 약 110~120GB 정도로 예상되는 상황이었으며, 무작정 분석을 돌리기엔 학원 PC의 사양이 문제가 되었다. 또한 시간적인 문제도 매우 큰 관계로 학습 시간을 단축하기 위해 CUDA 기반 GPU 연산을 검토하였다. 그러나 당시 사용한 학원 PC에는 CUDA를 지원하는 NVIDIA GPU가 설치되어 있지 않아 CPU만으로 학습을 진행해야 하는 상황이었다.&lt;/p&gt;
&lt;p data-end=&quot;32&quot; data-start=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;또한 영상 패치를 여러 연산 장치에 분산하는 과정에서 도로의 경계와 연속성이 충분히 유지되는지 우려되어 강사님께 해당 방법에 대한 문의를 진행하였다.&lt;/p&gt;
&lt;p data-end=&quot;32&quot; data-start=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;nbsp;이 과정에서 여러 대의 PC를 활용하여 서울시 자치구별 학습을 분담하는 방안도 검토하였다. 우선 1개 구 분량의 데이터로 학습한 U-Net 모델을 기준 모델로 저장한 뒤, 동일한 모델을 각 PC에 배포하고 서로 다른 자치구의 정사영상으로 추가 학습하는 방식이다. 이후 자치구별 모델을 통합하고 서울시 전체 영상으로 다시 미세조정하는 방안을 구상하였다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;nbsp;다만 서로 다른 데이터로 독립적으로 학습된 신경망의 가중치를 단순 평균하는 방식은 항상 안정적인 성능을 보장하지 않는다. 따라서 실제 적용 시에는 자치구별 학습 데이터를 순차적으로 사용하여 하나의 모델을 추가 학습하거나, 전체 데이터를 통합한 뒤 동일한 모델을 미세조정하는 방식과 비교 검증할 필요가 있다고 판단하였다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;nbsp;따라서 베이스 모델을 위 종로구 정사영상을 이용한 최초 학습은 약 9시간이 소요될 것으로 예상되었다. 그러나 실행 중 Zoom과 QGIS 등 CPU와 메모리를 사용하는 프로그램이 함께 실행되고 있음을 확인하였다. 불필요한 프로그램을 종료한 후 전체 학습에는 약 7시간이 소요되었다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;912&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdIbMl/dJMcabrArtv/Oz5W6h40qLbHKohJcDHvek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdIbMl/dJMcabrArtv/Oz5W6h40qLbHKohJcDHvek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdIbMl/dJMcabrArtv/Oz5W6h40qLbHKohJcDHvek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdIbMl%2FdJMcabrArtv%2FOz5W6h40qLbHKohJcDHvek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1300&quot; height=&quot;912&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;912&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;▲ 표준노드링크의 링크 shp&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1182&quot; data-origin-height=&quot;876&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYzuEt/dJMcafOm03q/NHkjGDVZe9cIC3xePN8EdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYzuEt/dJMcafOm03q/NHkjGDVZe9cIC3xePN8EdK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYzuEt/dJMcafOm03q/NHkjGDVZe9cIC3xePN8EdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYzuEt%2FdJMcafOm03q%2FNHkjGDVZe9cIC3xePN8EdK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1182&quot; height=&quot;876&quot; data-origin-width=&quot;1182&quot; data-origin-height=&quot;876&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;▲ 표준링크를 이용한 학습/추출 결과&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;본 과제의 목표는 기존 표준노드링크에 포함되지 않은 생활도로와 골목길까지 정사영상으로부터 자동 추출하는 것이었다. 그러나 표준노드링크는 도로 외곽선(Line) 형태의 벡터 데이터이므로, U-Net과 같은 의미론적 분할 모델에서 요구하는 픽셀 단위의 도로 라벨로 직접 활용하기에는 한계가 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이에 따라 표준노드링크를 일정 폭의 Polygon으로 변환하여 도로 영역을 생성하고, Polygon 내부의 픽셀을 도로 클래스로 학습시키는 방식을 적용하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이를 통해 모델이 단순히 링크의 위치를 기억하는 것이 아니라, 정사영상에서 나타나는 도로의 시각적 특성을 학습하여 표준링크에 존재하지 않는 미세도로까지 일반화할 수 있는지를 확인하고자 하였다.&lt;/p&gt;
&lt;p data-end=&quot;157&quot; data-start=&quot;29&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;157&quot; data-start=&quot;29&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;초기 모델의 탐지 결과를 확인한 결과 표준노드링크가 구축된 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;간선도로와 광폭도로는 비교적 안정적으로 분할&lt;/span&gt;되었다. 도로 폭이 넓고 포장면이 연속적으로 나타나는 구간에서는 예측 경계도 실제 도로 형태와 대체로 유사하게 형성되었다.&lt;/p&gt;
&lt;p data-end=&quot;378&quot; data-start=&quot;165&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;반면, &lt;span style=&quot;background-color: #f6e199;&quot;&gt;주거지 내부의 골목길이나 건물 사이의 협소도로는 상당 부분 누락&lt;/span&gt;되었다. 이러한 구간은 도로 폭이 좁고 건물 그림자, 주차 차량, 보도 및 인접 지붕과의 경계가 복잡하여 영상만으로 도로의 시각적 특징을 구분하기 어려웠다. 또한 학습 라벨로 사용한 표준노드링크가 주로 주요 도로 중심으로 구축되어 있기에 미세도로에 대한 학습 정보가 충분하지 않았던 점도 원인으로 판단하였다.&lt;/p&gt;
&lt;p data-end=&quot;378&quot; data-start=&quot;165&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;549&quot; data-start=&quot;386&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;즉, 초기 모델은 &lt;b&gt;주요 도로를 탐지하는 데에는 일정 수준의 성능을 보였으나, 보행 네트워크 보완에 필요한 골목길과 생활도로까지 추출하기에는 한계&lt;/b&gt;가 있었다. 이에 따라 이후 단계에서는 미세도로를 보다 강하게 반영할 수 있는 추가 학습 라벨을 구축하고 모델을 미세조정할 필요가 있다고 판단하였다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-end=&quot;310&quot; data-start=&quot;127&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;310&quot; data-start=&quot;127&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;미세도로에 대한 학습 정보를 보완하기 위해, 다음 단계에서는 세분류 토지피복지도의 도로 클래스를 추가 학습 라벨로 활용하였다. 기존 표준노드링크 기반 학습에서 가장 우수한 성능을 보인 모델을 초기 모델로 불러온 뒤, 세분류 토지피복지도에서 도로로 분류된 영역을 정답 라벨로 사용하여 Fine-tuning을 수행하고자 하였다.&lt;/p&gt;
&lt;p data-end=&quot;432&quot; data-start=&quot;318&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;432&quot; data-start=&quot;318&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;세분류 토지피복지도는 표준노드링크에 비해 골목길과 생활도로가 더 세밀하게 표현되어 있다는 장점이 있었다. 따라서 기존 모델이 충분히 학습하지 못했던 협소도로의 특징을 보완할 수 있을 것으로 기대하였다.&lt;/p&gt;
&lt;p data-end=&quot;432&quot; data-start=&quot;318&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;432&quot; data-start=&quot;318&quot; data-ke-size=&quot;size16&quot;&gt;학습결과 (펼치기)&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1785485075367&quot; class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;==============================================================================
세분류 토지피복도 도로 기반 U-Net 미세조정 시작
토지피복도 폴더: C:\Users\itwill\Desktop\세분류
도로 조건: L3_CODE=154
기존 모델: D:\Final\02_Output\Jongro_Ortho_Road_Segmentation_v3\04_road_unet.keras
출력 폴더: D:\Final\02_Output\Jongro_Landcover_Road_Finetune
==============================================================================

▶ 정사영상 모자이크 준비 시작 | RAM 1.19GB / 시스템 47%
기존 정사영상 모자이크 재사용: D:\Final\02_Output\Jongro_Ortho_Road_Segmentation_v3\01_jongro_orthomosaic_5179_050cm.tif
✓ 정사영상 모자이크 준비 완료: 0.0초 (0.00분) | RAM 1.19GB / 시스템 47%
PROJ DATA: C:\Users\itwill\anaconda3\Lib\site-packages\rasterio\proj_data
TensorFlow GPU: []
Mixed precision 비활성화
모자이크 CRS: EPSG:5179
모자이크 크기: 17,827 &amp;times; 22,464
해상도: 0.500m &amp;times; 0.500m

▶ 교차 도엽 도로 추출 시작 | RAM 1.19GB / 시스템 47%
세분류 SHP 전체 수: 139
정사영상과 교차하는 도엽 선별:   0%|          | 0/139 [00:00&amp;lt;?, ?file/s]
정사영상과 교차하는 SHP: 37개
 - 37608038.shp
 - 37608039.shp
 - 37608040.shp
 - 37608047.shp
 - 37608048.shp
 - 37608049.shp
 - 37608050.shp
 - 37608057.shp
 - 37608058.shp
 - 37608059.shp
 - 37608060.shp
 - 37608067.shp
 - 37608068.shp
 - 37608069.shp
 - 37608070.shp
 - 37608077.shp
 - 37608078.shp
 - 37608079.shp
 - 37608080.shp
 - 37608087.shp
 - 37608088.shp
 - 37608089.shp
 - 37608090.shp
 - 37705031.shp
 - 37705032.shp
 - 37705041.shp
 - 37705042.shp
 - 37705051.shp
 - 37705052.shp
 - 37705061.shp
 - 37705062.shp
 - 37705071.shp
 - 37705072.shp
 - 37705081.shp
 - 37705082.shp
 - 37608039.shp
 - 37608049.shp
L3_CODE=154 도로 추출:   0%|          | 0/37 [00:00&amp;lt;?, ?file/s]
추출된 도로 폴리곤 수: 6,546
도로 폴리곤 총면적: 27,134,246.3㎡
클립 도로 폴리곤 저장: D:\Final\02_Output\Jongro_Landcover_Road_Finetune\02_landcover_road_polygons_clipped.gpkg
✓ 교차 도엽 도로 추출 완료: 57.4초 (0.96분) | RAM 1.26GB / 시스템 47%

▶ 토지피복도 도로 마스크 rasterize 시작 | RAM 1.26GB / 시스템 47%
토지피복도 도로 rasterize:   0%|          | 0/6546 [00:00&amp;lt;?, ?geom/s]
✓ 토지피복도 도로 마스크 rasterize 완료: 39.2초 (0.65분) | RAM 1.66GB / 시스템 51%
학습 마스크 도로 픽셀: 117,473,400 (29.3342%)

▶ 밴드 정규화 통계 시작 | RAM 2.41GB / 시스템 56%
밴드별 2~98 percentile: [(0.0, 181.0), (0.0, 190.0), (0.0, 176.0)]
✓ 밴드 정규화 통계 완료: 9.6초 (0.16분) | RAM 2.81GB / 시스템 58%

▶ 학습&amp;middot;검증 패치 구성 시작 | RAM 2.81GB / 시스템 58%
학습 패치 좌표 검사:   0%|          | 0/24325 [00:00&amp;lt;?, ?patch/s]
양성 패치: 12,069
배경 패치 사용: 3,017
학습 좌표: 12,021
검증 좌표: 3,065
✓ 학습&amp;middot;검증 패치 구성 완료: 39.0초 (0.65분) | RAM 0.03GB / 시스템 46%

▶ 토지피복도 라벨 미세조정 학습 시작 | RAM 0.03GB / 시스템 46%
Epoch 1/8
3006/3006 ━━━━━━━━━━━━━━━━━━━━ 0s 521ms/step - accuracy: 0.7699 - dice_coefficient: 0.5082 - loss: 1.4282 - pr_auc: 0.6696 - precision: 0.6759 - recall: 0.4918 - roc_auc: 0.8087    
Epoch 1: val_dice_coefficient improved from None to 0.58338, saving model to D:\Final\02_Output\Jongro_Landcover_Road_Finetune\04_road_unet_landcover_finetuned.keras

Epoch 1: finished saving model to D:\Final\02_Output\Jongro_Landcover_Road_Finetune\04_road_unet_landcover_finetuned.keras
3006/3006 ━━━━━━━━━━━━━━━━━━━━ 1687s 553ms/step - accuracy: 0.7699 - dice_coefficient: 0.5082 - loss: 1.4282 - pr_auc: 0.6696 - precision: 0.6759 - recall: 0.4918 - roc_auc: 0.8087 - val_accuracy: 0.7890 - val_dice_coefficient: 0.5834 - val_loss: 0.9397 - val_pr_auc: 0.7603 - val_precision: 0.7331 - val_recall: 0.5496 - val_roc_auc: 0.8708 - learning_rate: 1.0000e-05
Epoch 2/8
3006/3006 ━━━━━━━━━━━━━━━━━━━━ 0s 453ms/step - accuracy: 0.7801 - dice_coefficient: 0.5328 - loss: 1.2390 - pr_auc: 0.6980 - precision: 0.6768 - recall: 0.5533 - roc_auc: 0.8340   
Epoch 2: val_dice_coefficient did not improve from 0.58338
3006/3006 ━━━━━━━━━━━━━━━━━━━━ 1452s 483ms/step - accuracy: 0.7801 - dice_coefficient: 0.5328 - loss: 1.2390 - pr_auc: 0.6980 - precision: 0.6768 - recall: 0.5533 - roc_auc: 0.8340 - val_accuracy: 0.7903 - val_dice_coefficient: 0.5768 - val_loss: 0.9555 - val_pr_auc: 0.7686 - val_precision: 0.7598 - val_recall: 0.5171 - val_roc_auc: 0.8743 - learning_rate: 1.0000e-05
Epoch 3/8
3006/3006 ━━━━━━━━━━━━━━━━━━━━ 0s 455ms/step - accuracy: 0.7835 - dice_coefficient: 0.5449 - loss: 1.1693 - pr_auc: 0.7066 - precision: 0.6743 - recall: 0.5806 - roc_auc: 0.8419   
Epoch 3: val_dice_coefficient did not improve from 0.58338
3006/3006 ━━━━━━━━━━━━━━━━━━━━ 1463s 487ms/step - accuracy: 0.7835 - dice_coefficient: 0.5449 - loss: 1.1693 - pr_auc: 0.7066 - precision: 0.6743 - recall: 0.5806 - roc_auc: 0.8419 - val_accuracy: 0.7897 - val_dice_coefficient: 0.5722 - val_loss: 0.9127 - val_pr_auc: 0.7730 - val_precision: 0.7726 - val_recall: 0.4981 - val_roc_auc: 0.8788 - learning_rate: 1.0000e-05
Epoch 4/8
3006/3006 ━━━━━━━━━━━━━━━━━━━━ 0s 456ms/step - accuracy: 0.7862 - dice_coefficient: 0.5508 - loss: 1.1163 - pr_auc: 0.7161 - precision: 0.6705 - recall: 0.6071 - roc_auc: 0.8488   
Epoch 4: val_dice_coefficient did not improve from 0.58338
3006/3006 ━━━━━━━━━━━━━━━━━━━━ 1466s 488ms/step - accuracy: 0.7862 - dice_coefficient: 0.5508 - loss: 1.1163 - pr_auc: 0.7161 - precision: 0.6705 - recall: 0.6071 - roc_auc: 0.8488 - val_accuracy: 0.7941 - val_dice_coefficient: 0.5828 - val_loss: 0.8998 - val_pr_auc: 0.7763 - val_precision: 0.7618 - val_recall: 0.5312 - val_roc_auc: 0.8800 - learning_rate: 1.0000e-05
Epoch 5/8
3006/3006 ━━━━━━━━━━━━━━━━━━━━ 0s 448ms/step - accuracy: 0.7877 - dice_coefficient: 0.5562 - loss: 1.0712 - pr_auc: 0.7229 - precision: 0.6658 - recall: 0.6298 - roc_auc: 0.8537   
Epoch 5: val_dice_coefficient improved from 0.58338 to 0.58913, saving model to D:\Final\02_Output\Jongro_Landcover_Road_Finetune\04_road_unet_landcover_finetuned.keras

Epoch 5: finished saving model to D:\Final\02_Output\Jongro_Landcover_Road_Finetune\04_road_unet_landcover_finetuned.keras
3006/3006 ━━━━━━━━━━━━━━━━━━━━ 1439s 479ms/step - accuracy: 0.7877 - dice_coefficient: 0.5562 - loss: 1.0712 - pr_auc: 0.7229 - precision: 0.6658 - recall: 0.6298 - roc_auc: 0.8537 - val_accuracy: 0.7952 - val_dice_coefficient: 0.5891 - val_loss: 0.8726 - val_pr_auc: 0.7779 - val_precision: 0.7566 - val_recall: 0.5434 - val_roc_auc: 0.8817 - learning_rate: 1.0000e-05
Epoch 6/8
3006/3006 ━━━━━━━━━━━━━━━━━━━━ 0s 447ms/step - accuracy: 0.7894 - dice_coefficient: 0.5641 - loss: 1.0364 - pr_auc: 0.7293 - precision: 0.6631 - recall: 0.6487 - roc_auc: 0.8578   
Epoch 6: val_dice_coefficient improved from 0.58913 to 0.59639, saving model to D:\Final\02_Output\Jongro_Landcover_Road_Finetune\04_road_unet_landcover_finetuned.keras

Epoch 6: finished saving model to D:\Final\02_Output\Jongro_Landcover_Road_Finetune\04_road_unet_landcover_finetuned.keras
3006/3006 ━━━━━━━━━━━━━━━━━━━━ 1436s 478ms/step - accuracy: 0.7894 - dice_coefficient: 0.5641 - loss: 1.0364 - pr_auc: 0.7293 - precision: 0.6631 - recall: 0.6487 - roc_auc: 0.8578 - val_accuracy: 0.7982 - val_dice_coefficient: 0.5964 - val_loss: 0.8594 - val_pr_auc: 0.7822 - val_precision: 0.7525 - val_recall: 0.5631 - val_roc_auc: 0.8833 - learning_rate: 1.0000e-05
Epoch 7/8
3006/3006 ━━━━━━━━━━━━━━━━━━━━ 0s 446ms/step - accuracy: 0.7905 - dice_coefficient: 0.5653 - loss: 1.0219 - pr_auc: 0.7327 - precision: 0.6613 - recall: 0.6611 - roc_auc: 0.8603   
Epoch 7: val_dice_coefficient did not improve from 0.59639
3006/3006 ━━━━━━━━━━━━━━━━━━━━ 1431s 476ms/step - accuracy: 0.7905 - dice_coefficient: 0.5653 - loss: 1.0219 - pr_auc: 0.7327 - precision: 0.6613 - recall: 0.6611 - roc_auc: 0.8603 - val_accuracy: 0.7947 - val_dice_coefficient: 0.5857 - val_loss: 0.8802 - val_pr_auc: 0.7808 - val_precision: 0.7716 - val_recall: 0.5211 - val_roc_auc: 0.8828 - learning_rate: 1.0000e-05
Epoch 8/8
3006/3006 ━━━━━━━━━━━━━━━━━━━━ 0s 447ms/step - accuracy: 0.7910 - dice_coefficient: 0.5638 - loss: 1.0079 - pr_auc: 0.7342 - precision: 0.6598 - recall: 0.6697 - roc_auc: 0.8620   
Epoch 8: val_dice_coefficient improved from 0.59639 to 0.60129, saving model to D:\Final\02_Output\Jongro_Landcover_Road_Finetune\04_road_unet_landcover_finetuned.keras

Epoch 8: finished saving model to D:\Final\02_Output\Jongro_Landcover_Road_Finetune\04_road_unet_landcover_finetuned.keras
3006/3006 ━━━━━━━━━━━━━━━━━━━━ 1433s 477ms/step - accuracy: 0.7910 - dice_coefficient: 0.5638 - loss: 1.0079 - pr_auc: 0.7342 - precision: 0.6598 - recall: 0.6697 - roc_auc: 0.8620 - val_accuracy: 0.8001 - val_dice_coefficient: 0.6013 - val_loss: 0.8429 - val_pr_auc: 0.7836 - val_precision: 0.7442 - val_recall: 0.5844 - val_roc_auc: 0.8844 - learning_rate: 1.0000e-05
Restoring model weights from the end of the best epoch: 8.
✓ 토지피복도 라벨 미세조정 학습 완료: 11811.1초 (196.85분) | RAM 1.41GB / 시스템 54%

▶ 미세조정 모델 전체 영상 추론 시작 | RAM 1.41GB / 시스템 54%
전체 영상 추론:   0%|          | 0/6082 [00:00&amp;lt;?, ?batch/s]
✓ 미세조정 모델 전체 영상 추론 완료: 1170.1초 (19.50분) | RAM 3.24GB / 시스템 65%
확률 최소/평균/최대: 0.00000000 / 0.23502274 / 0.99900025

▶ 임계값 0.40 후처리 시작 | RAM 3.24GB / 시스템 65%
✓ 임계값 0.40 후처리 완료: 19.8초 (0.33분) | RAM 3.31GB / 시스템 62%

▶ 임계값 0.40 폴리곤 export 시작 | RAM 3.31GB / 시스템 62%
예측 마스크 폴리곤화: 0geom [00:00, ?geom/s]
✓ 임계값 0.40 폴리곤 export 완료: 13.0초 (0.22분) | RAM 3.93GB / 시스템 68%
threshold=0.40 | 도로 픽셀=77,780,021 | 폴리곤=3,301

▶ 임계값 0.50 후처리 시작 | RAM 3.33GB / 시스템 64%
✓ 임계값 0.50 후처리 완료: 19.1초 (0.32분) | RAM 3.55GB / 시스템 61%

▶ 임계값 0.50 폴리곤 export 시작 | RAM 3.55GB / 시스템 61%
예측 마스크 폴리곤화: 0geom [00:00, ?geom/s]
✓ 임계값 0.50 폴리곤 export 완료: 11.3초 (0.19분) | RAM 3.76GB / 시스템 62%
threshold=0.50 | 도로 픽셀=61,012,745 | 폴리곤=4,565

▶ 임계값 0.60 후처리 시작 | RAM 3.19GB / 시스템 59%
✓ 임계값 0.60 후처리 완료: 18.7초 (0.31분) | RAM 3.57GB / 시스템 61%

▶ 임계값 0.60 폴리곤 export 시작 | RAM 3.57GB / 시스템 61%
예측 마스크 폴리곤화: 0geom [00:00, ?geom/s]
✓ 임계값 0.60 폴리곤 export 완료: 11.8초 (0.20분) | RAM 3.68GB / 시스템 64%
threshold=0.60 | 도로 픽셀=48,051,276 | 폴리곤=4,895

==============================================================================
모든 처리 완료
결과 폴더: D:\Final\02_Output\Jongro_Landcover_Road_Finetune
임계값 비교표: D:\Final\02_Output\Jongro_Landcover_Road_Finetune\threshold_comparison_summary.csv
==============================================================================&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1342&quot; data-origin-height=&quot;930&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lVIym/dJMcajiRGtk/8DhGqQxHDetpRSe6Ibv74K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lVIym/dJMcajiRGtk/8DhGqQxHDetpRSe6Ibv74K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lVIym/dJMcajiRGtk/8DhGqQxHDetpRSe6Ibv74K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlVIym%2FdJMcajiRGtk%2F8DhGqQxHDetpRSe6Ibv74K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1342&quot; height=&quot;930&quot; data-origin-width=&quot;1342&quot; data-origin-height=&quot;930&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;▲ 세분류 토지 피복지도의 도로&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1150&quot; data-origin-height=&quot;828&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dbZJkb/dJMcaaMZVRX/OoVb0YJd4aZwnngdUkCCbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dbZJkb/dJMcaaMZVRX/OoVb0YJd4aZwnngdUkCCbK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dbZJkb/dJMcaaMZVRX/OoVb0YJd4aZwnngdUkCCbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdbZJkb%2FdJMcaaMZVRX%2FOoVb0YJd4aZwnngdUkCCbK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1150&quot; height=&quot;828&quot; data-origin-width=&quot;1150&quot; data-origin-height=&quot;828&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-start=&quot;440&quot; data-end=&quot;633&quot; data-ke-size=&quot;size16&quot;&gt;▲ 세분류 토지 피복지도의 도로를 이용한 학습/탐지 결과&lt;/p&gt;
&lt;p data-start=&quot;440&quot; data-end=&quot;633&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&amp;nbsp;그러나 세분류 토지피복지도의 도로 클래스는 차량이나 보행자의 실제 이동 경로만을 구분한 데이터가 아니었다. 일부 구간에서는 도로와 연결된 &lt;b&gt;광장, 주차장, 넓은 포장면&lt;/b&gt; 등도 함께 도로 영역으로 분류되어 있었다. 이 영역들을 그대로 정답 라벨로 사용하면 모델은 선형적인 통행로뿐 아니라 도로 주변의 넓은 포장 공간까지 모두 도로로 학습하게 된다.&lt;/p&gt;
&lt;p data-start=&quot;440&quot; data-end=&quot;633&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;470&quot; data-start=&quot;329&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;세분류 토지피복지도를 이용한 Fine-tuning 이후에는 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;기존 모델에서 누락되던 골목길과 협소도로가 이전보다 많이 탐지&lt;/span&gt;되는 것을 확인할 수 있었다. 이는 세분류 토지피복지도가 표준노드링크보다 훨씬 세밀한 도로 정보를 포함하고 있었기 때문으로 판단된다.&lt;/p&gt;
&lt;p data-end=&quot;647&quot; data-start=&quot;475&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그러나 예측 결과를 정사영상과 비교한 결과, &lt;span style=&quot;background-color: #f6e199;&quot;&gt;도로뿐 아니라 건물 주변의 넓은 포장면과 광장, 주차장 등이 함께 도로로 분류되는 현상이 확인&lt;/span&gt;되었다. 일부 구간에서는 건물 외곽을 따라 넓은 영역이 도로로 확장되거나, 실제 도로 폭보다 훨씬 넓게 예측되는 과대 분할이 발생하였다.&lt;/p&gt;
&lt;p data-end=&quot;647&quot; data-start=&quot;475&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;830&quot; data-start=&quot;652&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이는 세분류 토지피복지도의 도로 클래스가 실제 차량 통행로뿐 아니라 다양한 포장 공간을 함께 포함하고 있기 때문으로 판단된다. 즉, 모델은 라벨에 포함된 모든 영역을 도로의 특성으로 학습하였으며, 그 결과 미세도로 탐지 성능은 향상되었지만 도로 경계는 이전보다 거칠어지고 비도로 영역까지 함께 예측하는 경향이 나타났다.&lt;/p&gt;
&lt;p data-end=&quot;830&quot; data-start=&quot;652&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;993&quot; data-start=&quot;835&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;결과적으로 세분류 토지피복지도는 미세도로에 대한 공간 정보를 보완하는 데에는 효과적이었지만, 본 연구에서 목표로 하는 &lt;b&gt;'실제 통행 가능한 선형 도로'&lt;/b&gt;와 토지피복도에서 정의하는 &lt;b&gt;'도로 및 포장면'&lt;/b&gt;의 개념이 서로 일치하지 않아 추가적인 라벨 정제가 필요함을 확인하였다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-end=&quot;1055&quot; data-start=&quot;976&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1055&quot; data-start=&quot;976&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 문제를 해결하기 위해 도로 Polygon 전체를 정답으로 사용하는 대신, &lt;b&gt;통행 가능한 영역만 학습시키는 방법&lt;/b&gt;을 고민하기 시작하였다.&lt;/p&gt;
&lt;p data-end=&quot;1162&quot; data-start=&quot;1057&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;가장 먼저 떠올린 아이디어는 &lt;b&gt;도로 Polygon 내부에서 중심선을 추출한 뒤, 중심선을 기준으로 약 2m 정도의 얇은 Buffer를 생성하여 새로운 학습 라벨로 사용하는 것&lt;/b&gt;이었다.&lt;/p&gt;
&lt;p data-end=&quot;1246&quot; data-start=&quot;1164&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이렇게 하면 광장이나 넓은 포장면은 대부분 제외하면서도 실제 차량이나 보행자가 이용하는 도로 중심부만 모델에게 학습시킬 수 있을 것으로 기대하였다.&lt;/p&gt;
&lt;p data-end=&quot;1246&quot; data-start=&quot;1164&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1339&quot; data-start=&quot;1277&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;처음에는 Polygon 내부에서 중심선을 생성하는 방법으로 &lt;b&gt;Voronoi 기반 중심선 추출&lt;/b&gt;을 검토하였다.&lt;/p&gt;
&lt;p data-end=&quot;1465&quot; data-start=&quot;1341&quot; data-ke-size=&quot;size16&quot;&gt;그러나 실제 적용 결과 중심선이 매우 잘게 끊어지고 불필요한 가지(branch)가 많이 생성되었다. 특히 복잡한 형태의 교차로나 굴곡이 있는 도로에서는 중심선이 불안정하게 생성되어 학습 라벨로 사용하기에는 적합하지 않았다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1271&quot; data-origin-height=&quot;870&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d9oxA2/dJMcabZuaZP/jOSo8bn6LQZgVKta0mlTVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d9oxA2/dJMcabZuaZP/jOSo8bn6LQZgVKta0mlTVk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d9oxA2/dJMcabZuaZP/jOSo8bn6LQZgVKta0mlTVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd9oxA2%2FdJMcabZuaZP%2FjOSo8bn6LQZgVKta0mlTVk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1271&quot; height=&quot;870&quot; data-origin-width=&quot;1271&quot; data-origin-height=&quot;870&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;1246&quot; data-start=&quot;1164&quot; data-ke-size=&quot;size16&quot;&gt;▲ Voronio 기반 중심선 추출 결과&lt;/p&gt;
&lt;p data-end=&quot;1246&quot; data-start=&quot;1164&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1585&quot; data-start=&quot;1522&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이후 여러 중심선 추출 방법을 비교한 결과 &lt;b&gt;Skeletonization(골격화)&lt;/b&gt; 기법을 적용하기로 하였다.&lt;/p&gt;
&lt;p data-end=&quot;1745&quot; data-start=&quot;1587&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Skeleton은 Polygon 내부를 한 픽셀 두께의 중심선으로 점진적으로 수축시키면서 전체 연결성을 최대한 유지하는 방식이다. 따라서 Voronoi보다 실제 도로 중심을 안정적으로 표현할 수 있었으며, 복잡한 도로 형태에서도 끊김이 적고 연속성이 높은 중심선을 생성할 수 있었다.&lt;/p&gt;
&lt;p data-end=&quot;1807&quot; data-start=&quot;1747&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이를 기반으로 중심선 주변에 약 2m Buffer를 생성하여 새로운 학습 라벨을 구축하는 실험을 진행하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;869&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/35B9V/dJMcadpkTEc/GEaAXeoM1H5Y8pk4JEg6q1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/35B9V/dJMcadpkTEc/GEaAXeoM1H5Y8pk4JEg6q1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/35B9V/dJMcadpkTEc/GEaAXeoM1H5Y8pk4JEg6q1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F35B9V%2FdJMcadpkTEc%2FGEaAXeoM1H5Y8pk4JEg6q1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1206&quot; height=&quot;869&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;869&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;1246&quot; data-start=&quot;1164&quot; data-ke-size=&quot;size16&quot;&gt;▲ Skeletonization 기법을 적용한 중심선 추출 결과&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c9rtqo/dJMcag0EpwW/3npm4A0w17mTRPzRlmJZwK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c9rtqo/dJMcag0EpwW/3npm4A0w17mTRPzRlmJZwK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c9rtqo/dJMcag0EpwW/3npm4A0w17mTRPzRlmJZwK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc9rtqo%2FdJMcag0EpwW%2F3npm4A0w17mTRPzRlmJZwK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;1246&quot; data-start=&quot;1164&quot; data-ke-size=&quot;size16&quot;&gt;▲ Voronio 기반 중심선과 Skeletonization 기반 중심선 차이 (ChatGPT 사용 그림)&lt;/p&gt;
&lt;p data-end=&quot;1246&quot; data-start=&quot;1164&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1246&quot; data-start=&quot;1164&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;해당 과정 이후 생성된 도로 라벨을 정사영상과 대조하며 수작업으로 검수하였다. 이 과정에서 &lt;b&gt;수목에 가려져 토지피복도에서는 도로로 적절히 표현되지 않았으나 실제 통행이 가능한 임도와 같은 예외 사례&lt;/b&gt;를 확인하였으며, 반대로 세분류 토지피복지도 구축 당시에는 &lt;b&gt;도로로 분류되었지만 현재는 공사, 재개발 또는 토지 이용 변화로 인해 더 이상 도로로 사용되지 않는 구간&lt;/b&gt;은 학습 라벨에서 제외하였다.&lt;/p&gt;
&lt;p data-end=&quot;1246&quot; data-start=&quot;1164&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 코드 (펼치기) ▼&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1785465660097&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# -*- coding: utf-8 -*-
&quot;&quot;&quot;
세분류 토지피복 도로 폴리곤 &amp;rarr; 타일 기반 중심선 추출
======================================================================

핵심 흐름
    도로 폴리곤
    &amp;rarr; 타일별 조회
    &amp;rarr; 2m 래스터화
    &amp;rarr; 형태학적 잡음 정리
    &amp;rarr; skeletonize
    &amp;rarr; 중심선 벡터화
    &amp;rarr; 짧은 가지 제거
    &amp;rarr; 타일 경계 중복 제거
    &amp;rarr; GPKG 저장

특징
- Voronoi 방식 사용 안 함
- 서울 전체 래스터를 한 번에 만들지 않음
- 타일 겹침 영역을 사용하여 타일 경계 단절 최소화
- Jupyter와 .py 실행 모두 가능
- tqdm 진행률과 예상 잔여 시간 표시

필수 패키지
    pip install -U geopandas pyogrio rasterio shapely scikit-image scipy tqdm

권장
    shapely &amp;gt;= 2.0
    geopandas &amp;gt;= 0.14
    rasterio &amp;gt;= 1.3
    scikit-image &amp;gt;= 0.22
&quot;&quot;&quot;

from __future__ import annotations

import math
import os
import time
import warnings
from pathlib import Path
from typing import Iterable

import geopandas as gpd
import numpy as np
import pandas as pd
from affine import Affine
from rasterio.features import rasterize
from shapely import make_valid
from shapely.geometry import (
    LineString,
    MultiLineString,
    Polygon,
    box,
)
from shapely.ops import linemerge, unary_union
from skimage.morphology import (
    binary_closing,
    binary_opening,
    disk,
    remove_small_holes,
    remove_small_objects,
    skeletonize,
)
from tqdm import tqdm

warnings.filterwarnings(&quot;ignore&quot;, category=UserWarning)


# =============================================================================
# 1. 사용자 설정
# =============================================================================

INPUT_FILE = Path(
    r&quot;C:\Users\itwill\Desktop\세분류\세분류_도로_154_통합.shp&quot;
)

OUTPUT_GPKG = Path(
    r&quot;C:\Users\itwill\Desktop\세분류\road_centerline_tile_2m_preview.gpkg&quot;
)

TARGET_CRS = &quot;EPSG:5179&quot;

# -------------------------------------------------------------------------
# 1차 미리보기 권장값
# -------------------------------------------------------------------------

# 픽셀 크기(m)
RESOLUTION_M = 2.0

# 한 타일의 본체 크기(픽셀)
# 2048 &amp;times; 2m = 약 4.096km
TILE_SIZE_PIXELS = 2048

# 타일 외곽에 추가로 읽는 중첩 폭(픽셀)
# 중심선은 마지막에 본체 영역으로 잘라서 중복을 제거함
TILE_OVERLAP_PIXELS = 64

# 도로 면의 작은 틈을 메우는 closing 반경(픽셀)
# 1픽셀 = 2m이므로 1이면 약 2m
CLOSING_RADIUS_PIXELS = 1

# 고립된 작은 돌기 제거용 opening 반경(픽셀)
# 너무 크게 하면 좁은 도로가 사라질 수 있으므로 기본 0
OPENING_RADIUS_PIXELS = 0

# 작은 도로 조각 제거 면적(㎡)
MIN_ROAD_COMPONENT_AREA_M2 = 40.0

# 작은 내부 구멍을 메우는 최대 면적(㎡)
MAX_HOLE_AREA_M2 = 36.0

# 중심선의 짧은 가지 제거 기준(m)
MIN_BRANCH_LENGTH_M = 20.0

# 최종 선 단순화 허용오차(m)
SIMPLIFY_TOLERANCE_M = 1.5

# 입력 폴리곤 최소 면적(㎡)
MIN_INPUT_POLYGON_AREA_M2 = 10.0

# 래스터화 시 픽셀 중심만 포함할지 여부
# False가 도로 폭 과대 추출을 줄이는 데 유리
ALL_TOUCHED = False

# 최종 전체 선 병합 여부
# True면 타일 경계의 중복&amp;middot;분절을 정리하지만 마지막 단계가 조금 오래 걸릴 수 있음
GLOBAL_MERGE = True

# 디버그용 타일 경계 레이어 저장
SAVE_TILE_GRID = False

# 오류 CSV 저장
SAVE_ERROR_CSV = True


# =============================================================================
# 2. 입력 전처리
# =============================================================================

def load_road_polygons() -&amp;gt; gpd.GeoDataFrame:
    if not INPUT_FILE.exists():
        raise FileNotFoundError(f&quot;입력 파일이 없습니다:\n{INPUT_FILE}&quot;)

    print(f&quot;입력 읽는 중: {INPUT_FILE}&quot;)

    roads = gpd.read_file(INPUT_FILE, engine=&quot;pyogrio&quot;)

    if roads.empty:
        raise ValueError(&quot;입력 파일에 객체가 없습니다.&quot;)

    if roads.crs is None:
        raise ValueError(
            &quot;입력 데이터에 좌표계가 없습니다. &quot;
            &quot;QGIS에서 올바른 좌표계를 먼저 지정하세요.&quot;
        )

    roads = roads.to_crs(TARGET_CRS)

    roads = roads[
        roads.geometry.notna()
        &amp;amp; ~roads.geometry.is_empty
    ].copy()

    roads[&quot;geometry&quot;] = roads.geometry.apply(make_valid)

    roads = roads.explode(
        index_parts=False,
        ignore_index=True,
    )

    roads = roads[
        roads.geometry.geom_type.isin([&quot;Polygon&quot;, &quot;MultiPolygon&quot;])
    ].copy()

    roads = roads[
        roads.geometry.area &amp;gt;= MIN_INPUT_POLYGON_AREA_M2
    ].copy()

    roads = roads.reset_index(drop=True)

    if roads.empty:
        raise ValueError(&quot;유효한 도로 폴리곤이 없습니다.&quot;)

    # 공간 인덱스 미리 생성
    _ = roads.sindex

    print(f&quot;처리 대상 폴리곤: {len(roads):,}개&quot;)
    print(f&quot;총 도로 면적: {roads.geometry.area.sum():,.1f}㎡&quot;)

    return roads


# =============================================================================
# 3. 타일 생성
# =============================================================================

def align_down(value: float, step: float) -&amp;gt; float:
    return math.floor(value / step) * step


def align_up(value: float, step: float) -&amp;gt; float:
    return math.ceil(value / step) * step


def build_tile_grid(
    roads: gpd.GeoDataFrame,
) -&amp;gt; list[dict]:
    minx, miny, maxx, maxy = roads.total_bounds

    core_size_m = TILE_SIZE_PIXELS * RESOLUTION_M
    overlap_m = TILE_OVERLAP_PIXELS * RESOLUTION_M

    grid_minx = align_down(minx, core_size_m)
    grid_miny = align_down(miny, core_size_m)
    grid_maxx = align_up(maxx, core_size_m)
    grid_maxy = align_up(maxy, core_size_m)

    n_cols = int(round((grid_maxx - grid_minx) / core_size_m))
    n_rows = int(round((grid_maxy - grid_miny) / core_size_m))

    tiles = []
    tile_id = 0

    for row in range(n_rows):
        for col in range(n_cols):
            core_minx = grid_minx + col * core_size_m
            core_miny = grid_miny + row * core_size_m
            core_maxx = core_minx + core_size_m
            core_maxy = core_miny + core_size_m

            core_geom = box(
                core_minx,
                core_miny,
                core_maxx,
                core_maxy,
            )

            # 도로가 전혀 없는 타일은 제외
            candidate_idx = list(
                roads.sindex.query(
                    core_geom,
                    predicate=&quot;intersects&quot;,
                )
            )

            if not candidate_idx:
                continue

            tile_id += 1

            tiles.append(
                {
                    &quot;tile_id&quot;: tile_id,
                    &quot;row&quot;: row,
                    &quot;col&quot;: col,
                    &quot;core_bounds&quot;: (
                        core_minx,
                        core_miny,
                        core_maxx,
                        core_maxy,
                    ),
                    &quot;expanded_bounds&quot;: (
                        core_minx - overlap_m,
                        core_miny - overlap_m,
                        core_maxx + overlap_m,
                        core_maxy + overlap_m,
                    ),
                    &quot;core_geometry&quot;: core_geom,
                }
            )

    print(f&quot;도로가 포함된 처리 타일: {len(tiles):,}개&quot;)

    return tiles


# =============================================================================
# 4. 타일 래스터화 및 형태학적 정리
# =============================================================================

def rasterize_tile(
    roads: gpd.GeoDataFrame,
    expanded_bounds: tuple[float, float, float, float],
) -&amp;gt; tuple[np.ndarray, Affine]:
    minx, miny, maxx, maxy = expanded_bounds

    width = int(round((maxx - minx) / RESOLUTION_M))
    height = int(round((maxy - miny) / RESOLUTION_M))

    transform = Affine(
        RESOLUTION_M,
        0.0,
        minx,
        0.0,
        -RESOLUTION_M,
        maxy,
    )

    tile_geom = box(minx, miny, maxx, maxy)

    candidate_idx = list(
        roads.sindex.query(
            tile_geom,
            predicate=&quot;intersects&quot;,
        )
    )

    if not candidate_idx:
        return np.zeros((height, width), dtype=bool), transform

    clipped_geometries = []

    for geom in roads.geometry.iloc[candidate_idx]:
        clipped = geom.intersection(tile_geom)

        if clipped.is_empty:
            continue

        if clipped.geom_type == &quot;Polygon&quot;:
            clipped_geometries.append(clipped)

        elif clipped.geom_type == &quot;MultiPolygon&quot;:
            clipped_geometries.extend(
                part
                for part in clipped.geoms
                if not part.is_empty
            )

    if not clipped_geometries:
        return np.zeros((height, width), dtype=bool), transform

    mask = rasterize(
        (
            (geom, 1)
            for geom in clipped_geometries
        ),
        out_shape=(height, width),
        transform=transform,
        fill=0,
        dtype=&quot;uint8&quot;,
        all_touched=ALL_TOUCHED,
    ).astype(bool)

    min_component_pixels = max(
        1,
        int(
            round(
                MIN_ROAD_COMPONENT_AREA_M2
                / (RESOLUTION_M ** 2)
            )
        ),
    )

    max_hole_pixels = max(
        1,
        int(
            round(
                MAX_HOLE_AREA_M2
                / (RESOLUTION_M ** 2)
            )
        ),
    )

    mask = remove_small_objects(
        mask,
        min_size=min_component_pixels,
        connectivity=2,
    )

    mask = remove_small_holes(
        mask,
        area_threshold=max_hole_pixels,
        connectivity=2,
    )

    if CLOSING_RADIUS_PIXELS &amp;gt; 0:
        mask = binary_closing(
            mask,
            footprint=disk(CLOSING_RADIUS_PIXELS),
        )

    if OPENING_RADIUS_PIXELS &amp;gt; 0:
        mask = binary_opening(
            mask,
            footprint=disk(OPENING_RADIUS_PIXELS),
        )

    return mask, transform


# =============================================================================
# 5. Skeleton 픽셀 &amp;rarr; 벡터 선
# =============================================================================

def pixel_center(
    row: int,
    col: int,
    transform: Affine,
) -&amp;gt; tuple[float, float]:
    x = transform.c + (col + 0.5) * transform.a
    y = transform.f + (row + 0.5) * transform.e
    return float(x), float(y)


def skeleton_to_segments(
    skeleton: np.ndarray,
    transform: Affine,
) -&amp;gt; list[LineString]:
    &quot;&quot;&quot;
    8방향 연결에서 중복 없이 다음 4방향만 확인:
      오른쪽, 아래왼쪽, 아래, 아래오른쪽
    &quot;&quot;&quot;
    rows, cols = np.nonzero(skeleton)

    if len(rows) == 0:
        return []

    foreground = set(zip(rows.tolist(), cols.tolist()))

    forward_neighbors = (
        (0, 1),
        (1, -1),
        (1, 0),
        (1, 1),
    )

    segments = []

    for row, col in foreground:
        start_xy = pixel_center(row, col, transform)

        for drow, dcol in forward_neighbors:
            nrow = row + drow
            ncol = col + dcol

            if (nrow, ncol) not in foreground:
                continue

            end_xy = pixel_center(nrow, ncol, transform)

            segments.append(
                LineString([start_xy, end_xy])
            )

    return segments


def flatten_lines(geometry) -&amp;gt; list[LineString]:
    if geometry is None or geometry.is_empty:
        return []

    if isinstance(geometry, LineString):
        return [geometry]

    if isinstance(geometry, MultiLineString):
        return list(geometry.geoms)

    if hasattr(geometry, &quot;geoms&quot;):
        lines = []

        for part in geometry.geoms:
            lines.extend(flatten_lines(part))

        return lines

    return []


def merge_and_filter_lines(
    segments: list[LineString],
    clip_geometry: Polygon,
) -&amp;gt; list[LineString]:
    if not segments:
        return []

    # 교차점에서 선을 분할한 뒤 연결 가능한 부분 병합
    noded = unary_union(segments)
    merged = linemerge(noded)

    candidate_lines = flatten_lines(merged)
    final_lines = []

    for line in candidate_lines:
        if line.is_empty:
            continue

        clipped = line.intersection(clip_geometry)

        for part in flatten_lines(clipped):
            if part.length &amp;lt; MIN_BRANCH_LENGTH_M:
                continue

            if SIMPLIFY_TOLERANCE_M &amp;gt; 0:
                part = part.simplify(
                    SIMPLIFY_TOLERANCE_M,
                    preserve_topology=False,
                )

            if (
                not part.is_empty
                and part.length &amp;gt;= MIN_BRANCH_LENGTH_M
            ):
                final_lines.append(part)

    return final_lines


# =============================================================================
# 6. 타일 처리
# =============================================================================

def process_one_tile(
    roads: gpd.GeoDataFrame,
    tile: dict,
) -&amp;gt; tuple[list[dict], dict | None]:
    tile_id = tile[&quot;tile_id&quot;]

    try:
        mask, transform = rasterize_tile(
            roads,
            tile[&quot;expanded_bounds&quot;],
        )

        if not mask.any():
            return [], None

        skeleton = skeletonize(
            mask,
            method=&quot;zhang&quot;,
        )

        segments = skeleton_to_segments(
            skeleton,
            transform,
        )

        lines = merge_and_filter_lines(
            segments,
            tile[&quot;core_geometry&quot;],
        )

        records = []

        for part_id, line in enumerate(lines, start=1):
            records.append(
                {
                    &quot;tile_id&quot;: tile_id,
                    &quot;part_id&quot;: part_id,
                    &quot;length_m&quot;: round(float(line.length), 2),
                    &quot;geometry&quot;: line,
                }
            )

        return records, None

    except Exception as exc:
        return [], {
            &quot;tile_id&quot;: tile_id,
            &quot;row&quot;: tile[&quot;row&quot;],
            &quot;col&quot;: tile[&quot;col&quot;],
            &quot;error&quot;: repr(exc),
        }


# =============================================================================
# 7. 전체 실행
# =============================================================================

def run_centerline_extraction(
    roads: gpd.GeoDataFrame,
    tiles: list[dict],
) -&amp;gt; tuple[gpd.GeoDataFrame, pd.DataFrame]:
    records = []
    errors = []

    print()
    print(&quot;=&quot; * 72)
    print(&quot;타일 기반 중심선 추출 시작&quot;)
    print(f&quot;해상도: {RESOLUTION_M}m&quot;)
    print(
        f&quot;타일 본체: {TILE_SIZE_PIXELS}px &quot;
        f&quot;({TILE_SIZE_PIXELS * RESOLUTION_M:,.0f}m)&quot;
    )
    print(
        f&quot;타일 중첩: {TILE_OVERLAP_PIXELS}px &quot;
        f&quot;({TILE_OVERLAP_PIXELS * RESOLUTION_M:,.0f}m)&quot;
    )
    print(f&quot;짧은 가지 제거: {MIN_BRANCH_LENGTH_M}m 미만&quot;)
    print(&quot;=&quot; * 72)

    start = time.perf_counter()

    for tile in tqdm(
        tiles,
        total=len(tiles),
        desc=&quot;타일 중심선 추출&quot;,
        unit=&quot;tile&quot;,
        dynamic_ncols=True,
    ):
        tile_records, error = process_one_tile(
            roads,
            tile,
        )

        records.extend(tile_records)

        if error is not None:
            errors.append(error)

    elapsed = time.perf_counter() - start

    print()
    print(f&quot;타일 처리 시간: {elapsed / 60:,.1f}분&quot;)

    if not records:
        raise RuntimeError(
            &quot;생성된 중심선이 없습니다. &quot;
            &quot;입력 좌표계와 설정값을 확인하세요.&quot;
        )

    lines = gpd.GeoDataFrame(
        records,
        crs=roads.crs,
    )

    error_df = pd.DataFrame(errors)

    print(f&quot;타일별 중심선 조각: {len(lines):,}개&quot;)
    print(f&quot;오류 타일: {len(error_df):,}개&quot;)

    return lines, error_df


# =============================================================================
# 8. 전체 병합 및 최종 정리
# =============================================================================

def global_merge_centerlines(
    tile_lines: gpd.GeoDataFrame,
) -&amp;gt; gpd.GeoDataFrame:
    if not GLOBAL_MERGE:
        result = tile_lines.copy()
        result[&quot;centerline_id&quot;] = np.arange(
            1,
            len(result) + 1,
            dtype=np.int64,
        )

        return result[
            [
                &quot;centerline_id&quot;,
                &quot;tile_id&quot;,
                &quot;length_m&quot;,
                &quot;geometry&quot;,
            ]
        ]

    print()
    print(&quot;타일 경계 중복 및 분절 정리 중...&quot;)

    merged_geometry = linemerge(
        unary_union(
            tile_lines.geometry.tolist()
        )
    )

    parts = flatten_lines(merged_geometry)

    records = []

    for line in parts:
        if line.is_empty:
            continue

        if line.length &amp;lt; MIN_BRANCH_LENGTH_M:
            continue

        if SIMPLIFY_TOLERANCE_M &amp;gt; 0:
            line = line.simplify(
                SIMPLIFY_TOLERANCE_M,
                preserve_topology=False,
            )

        if line.length &amp;lt; MIN_BRANCH_LENGTH_M:
            continue

        records.append(
            {
                &quot;length_m&quot;: round(float(line.length), 2),
                &quot;geometry&quot;: line,
            }
        )

    result = gpd.GeoDataFrame(
        records,
        crs=tile_lines.crs,
    )

    result[&quot;centerline_id&quot;] = np.arange(
        1,
        len(result) + 1,
        dtype=np.int64,
    )

    return result[
        [
            &quot;centerline_id&quot;,
            &quot;length_m&quot;,
            &quot;geometry&quot;,
        ]
    ]


# =============================================================================
# 9. 저장
# =============================================================================

def save_results(
    roads: gpd.GeoDataFrame,
    tiles: list[dict],
    centerlines: gpd.GeoDataFrame,
    errors: pd.DataFrame,
) -&amp;gt; None:
    OUTPUT_GPKG.parent.mkdir(
        parents=True,
        exist_ok=True,
    )

    if OUTPUT_GPKG.exists():
        OUTPUT_GPKG.unlink()

    print()
    print(&quot;GPKG 저장 중...&quot;)

    centerlines.to_file(
        OUTPUT_GPKG,
        layer=&quot;road_centerline_2m&quot;,
        driver=&quot;GPKG&quot;,
        engine=&quot;pyogrio&quot;,
    )

    if SAVE_TILE_GRID:
        tile_grid = gpd.GeoDataFrame(
            [
                {
                    &quot;tile_id&quot;: tile[&quot;tile_id&quot;],
                    &quot;row&quot;: tile[&quot;row&quot;],
                    &quot;col&quot;: tile[&quot;col&quot;],
                    &quot;geometry&quot;: tile[&quot;core_geometry&quot;],
                }
                for tile in tiles
            ],
            crs=roads.crs,
        )

        tile_grid.to_file(
            OUTPUT_GPKG,
            layer=&quot;processing_tiles&quot;,
            driver=&quot;GPKG&quot;,
            engine=&quot;pyogrio&quot;,
        )

    if SAVE_ERROR_CSV and not errors.empty:
        error_path = OUTPUT_GPKG.with_name(
            OUTPUT_GPKG.stem + &quot;_errors.csv&quot;
        )

        errors.to_csv(
            error_path,
            index=False,
            encoding=&quot;utf-8-sig&quot;,
        )

        print(f&quot;오류 CSV: {error_path}&quot;)

    print(f&quot;결과 파일: {OUTPUT_GPKG}&quot;)
    print(&quot;결과 레이어: road_centerline_2m&quot;)


# =============================================================================
# 10. 메인
# =============================================================================

def main() -&amp;gt; None:
    print(&quot;=&quot; * 72)
    print(&quot;도로 폴리곤 타일 기반 2m 중심선 추출&quot;)
    print(&quot;=&quot; * 72)

    roads = load_road_polygons()
    tiles = build_tile_grid(roads)

    tile_lines, errors = run_centerline_extraction(
        roads,
        tiles,
    )

    centerlines = global_merge_centerlines(
        tile_lines,
    )

    print()
    print(f&quot;최종 중심선 개수: {len(centerlines):,}개&quot;)
    print(
        f&quot;최종 중심선 총길이: &quot;
        f&quot;{centerlines.geometry.length.sum() / 1000:,.2f}km&quot;
    )

    save_results(
        roads,
        tiles,
        centerlines,
        errors,
    )

    print()
    print(&quot;전체 작업 완료&quot;)


if __name__ == &quot;__main__&quot;:
    main()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과 (펼치기) ▼&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1785485127617&quot; class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;PROJ DB 선택: C:\Users\itwill\anaconda3\Lib\site-packages\rasterio\proj_data\proj.db
PROJ DB minor: 6
==============================================================================
2개 정사영상 + 2m 중심선 약한 라벨 파인튜닝
==============================================================================
PROJ DATA: C:\Users\itwill\anaconda3\Lib\site-packages\rasterio\proj_data
TensorFlow GPU: []
기존 모델: D:\Final\02_Output\Jongro_Ortho_Road_Segmentation_v3\04_road_unet.keras
약한 라벨 중심선: D:\Final\03_sebunryu\2m_centerline.gpkg
출력 폴더: D:\Final\02_Output\Jongro_WeakLabel_Finetune_2Tiles

▶ 정사영상 2장 모자이크 시작 | RAM 1.52GB / 시스템 69%
기존 모자이크 재사용: D:\Final\02_Output\Jongro_WeakLabel_Finetune_2Tiles\01_two_tile_orthomosaic_5179_050cm.tif
✓ 정사영상 2장 모자이크 완료: 0.0초 (0.00분) | RAM 1.52GB / 시스템 69%
모자이크 CRS: EPSG:5179
모자이크 크기: 9,063 &amp;times; 11,274

▶ 중심선 2m 버퍼 약한 라벨 생성 시작 | RAM 1.52GB / 시스템 69%
자동 선택 중심선 레이어: road_centerline_tile_2m_preview__road_centerline_2m
영상 범위 중심선: 11,876개
중심선 총길이: 466.23km
약한 라벨 rasterize 입력:   0%|          | 0/11876 [00:00&amp;lt;?, ?geom/s]
약한 라벨 양성 픽셀 비율: 0.0854
✓ 중심선 2m 버퍼 약한 라벨 생성 완료: 2.9초 (0.05분) | RAM 1.62GB / 시스템 70%

▶ 밴드 정규화 통계 시작 | RAM 1.72GB / 시스템 70%
밴드별 2~98 percentile: [(0.0, 182.0), (0.0, 188.0), (0.0, 172.0)]
✓ 밴드 정규화 통계 완료: 0.0초 (0.00분) | RAM 1.72GB / 시스템 70%

▶ 학습&amp;middot;검증 패치 구성 시작 | RAM 1.72GB / 시스템 70%
파인튜닝 패치 검사:   0%|          | 0/6160 [00:00&amp;lt;?, ?patch/s]
양성 패치: 2,819
배경 패치 사용: 296
학습 패치: 2,648
검증 패치: 467
✓ 학습&amp;middot;검증 패치 구성 완료: 6.3초 (0.11분) | RAM 1.69GB / 시스템 69%

▶ 기존 베스트 모델 약한 라벨 파인튜닝 시작 | RAM 1.69GB / 시스템 69%
기존 모델 입력 형태: (None, 256, 256, 3)
Epoch 1/6
662/662 ━━━━━━━━━━━━━━━━━━━━ 0s 457ms/step - accuracy: 0.8923 - dice_coefficient: 0.1967 - loss: 1.3812 - pr_auc: 0.1458 - precision: 0.1998 - recall: 0.0670 - roc_auc: 0.6362   
Epoch 1: val_dice_coefficient improved from None to 0.18133, saving model to D:\Final\02_Output\Jongro_WeakLabel_Finetune_2Tiles\03_weaklabel_finetuned_best.keras

Epoch 1: finished saving model to D:\Final\02_Output\Jongro_WeakLabel_Finetune_2Tiles\03_weaklabel_finetuned_best.keras
662/662 ━━━━━━━━━━━━━━━━━━━━ 329s 483ms/step - accuracy: 0.8923 - dice_coefficient: 0.1967 - loss: 1.3812 - pr_auc: 0.1458 - precision: 0.1998 - recall: 0.0670 - roc_auc: 0.6362 - val_accuracy: 0.9106 - val_dice_coefficient: 0.1813 - val_loss: 1.2940 - val_pr_auc: 0.1457 - val_precision: 0.1940 - val_recall: 0.0091 - val_roc_auc: 0.6578 - learning_rate: 1.0000e-05
Epoch 2/6
662/662 ━━━━━━━━━━━━━━━━━━━━ 0s 465ms/step - accuracy: 0.9031 - dice_coefficient: 0.2014 - loss: 1.3221 - pr_auc: 0.1520 - precision: 0.2120 - recall: 0.0294 - roc_auc: 0.6529  
Epoch 2: val_dice_coefficient improved from 0.18133 to 0.19694, saving model to D:\Final\02_Output\Jongro_WeakLabel_Finetune_2Tiles\03_weaklabel_finetuned_best.keras

Epoch 2: finished saving model to D:\Final\02_Output\Jongro_WeakLabel_Finetune_2Tiles\03_weaklabel_finetuned_best.keras
662/662 ━━━━━━━━━━━━━━━━━━━━ 323s 487ms/step - accuracy: 0.9031 - dice_coefficient: 0.2014 - loss: 1.3221 - pr_auc: 0.1520 - precision: 0.2120 - recall: 0.0294 - roc_auc: 0.6529 - val_accuracy: 0.9017 - val_dice_coefficient: 0.1969 - val_loss: 1.2672 - val_pr_auc: 0.1590 - val_precision: 0.2388 - val_recall: 0.0603 - val_roc_auc: 0.6692 - learning_rate: 1.0000e-05
Epoch 3/6
662/662 ━━━━━━━━━━━━━━━━━━━━ 0s 449ms/step - accuracy: 0.9004 - dice_coefficient: 0.2101 - loss: 1.2868 - pr_auc: 0.1567 - precision: 0.2237 - recall: 0.0447 - roc_auc: 0.6610  
Epoch 3: val_dice_coefficient improved from 0.19694 to 0.20462, saving model to D:\Final\02_Output\Jongro_WeakLabel_Finetune_2Tiles\03_weaklabel_finetuned_best.keras

Epoch 3: finished saving model to D:\Final\02_Output\Jongro_WeakLabel_Finetune_2Tiles\03_weaklabel_finetuned_best.keras
662/662 ━━━━━━━━━━━━━━━━━━━━ 312s 471ms/step - accuracy: 0.9004 - dice_coefficient: 0.2101 - loss: 1.2868 - pr_auc: 0.1567 - precision: 0.2237 - recall: 0.0447 - roc_auc: 0.6610 - val_accuracy: 0.8954 - val_dice_coefficient: 0.2046 - val_loss: 1.2222 - val_pr_auc: 0.1685 - val_precision: 0.2501 - val_recall: 0.1020 - val_roc_auc: 0.6829 - learning_rate: 1.0000e-05
Epoch 4/6
662/662 ━━━━━━━━━━━━━━━━━━━━ 0s 461ms/step - accuracy: 0.8967 - dice_coefficient: 0.2085 - loss: 1.2771 - pr_auc: 0.1594 - precision: 0.2260 - recall: 0.0626 - roc_auc: 0.6653  
Epoch 4: val_dice_coefficient did not improve from 0.20462

Epoch 4: ReduceLROnPlateau reducing learning rate to 4.999999873689376e-06.
662/662 ━━━━━━━━━━━━━━━━━━━━ 321s 484ms/step - accuracy: 0.8967 - dice_coefficient: 0.2085 - loss: 1.2771 - pr_auc: 0.1594 - precision: 0.2260 - recall: 0.0626 - roc_auc: 0.6653 - val_accuracy: 0.8949 - val_dice_coefficient: 0.2038 - val_loss: 1.2336 - val_pr_auc: 0.1659 - val_precision: 0.2391 - val_recall: 0.0963 - val_roc_auc: 0.6815 - learning_rate: 1.0000e-05
Epoch 5/6
662/662 ━━━━━━━━━━━━━━━━━━━━ 0s 458ms/step - accuracy: 0.8975 - dice_coefficient: 0.2123 - loss: 1.2656 - pr_auc: 0.1609 - precision: 0.2286 - recall: 0.0604 - roc_auc: 0.6684  
Epoch 5: val_dice_coefficient improved from 0.20462 to 0.21061, saving model to D:\Final\02_Output\Jongro_WeakLabel_Finetune_2Tiles\03_weaklabel_finetuned_best.keras

Epoch 5: finished saving model to D:\Final\02_Output\Jongro_WeakLabel_Finetune_2Tiles\03_weaklabel_finetuned_best.keras

Epoch 5: ReduceLROnPlateau reducing learning rate to 2.499999936844688e-06.
662/662 ━━━━━━━━━━━━━━━━━━━━ 317s 479ms/step - accuracy: 0.8975 - dice_coefficient: 0.2123 - loss: 1.2656 - pr_auc: 0.1609 - precision: 0.2286 - recall: 0.0604 - roc_auc: 0.6684 - val_accuracy: 0.8920 - val_dice_coefficient: 0.2106 - val_loss: 1.2243 - val_pr_auc: 0.1706 - val_precision: 0.2422 - val_recall: 0.1142 - val_roc_auc: 0.6883 - learning_rate: 5.0000e-06
Epoch 6/6
662/662 ━━━━━━━━━━━━━━━━━━━━ 0s 450ms/step - accuracy: 0.8954 - dice_coefficient: 0.2102 - loss: 1.2613 - pr_auc: 0.1620 - precision: 0.2290 - recall: 0.0702 - roc_auc: 0.6712  
Epoch 6: val_dice_coefficient improved from 0.21061 to 0.21108, saving model to D:\Final\02_Output\Jongro_WeakLabel_Finetune_2Tiles\03_weaklabel_finetuned_best.keras

Epoch 6: finished saving model to D:\Final\02_Output\Jongro_WeakLabel_Finetune_2Tiles\03_weaklabel_finetuned_best.keras
662/662 ━━━━━━━━━━━━━━━━━━━━ 312s 472ms/step - accuracy: 0.8954 - dice_coefficient: 0.2102 - loss: 1.2613 - pr_auc: 0.1620 - precision: 0.2290 - recall: 0.0702 - roc_auc: 0.6712 - val_accuracy: 0.8939 - val_dice_coefficient: 0.2111 - val_loss: 1.1925 - val_pr_auc: 0.1720 - val_precision: 0.2471 - val_recall: 0.1080 - val_roc_auc: 0.6927 - learning_rate: 2.5000e-06
Restoring model weights from the end of the best epoch: 6.
✓ 기존 베스트 모델 약한 라벨 파인튜닝 완료: 1916.0초 (31.93분) | RAM 1.66GB / 시스템 65%

▶ 기존 모델 동일 영상 추론 시작 | RAM 1.66GB / 시스템 65%
04_baseline 전체 추론:   0%|          | 0/1540 [00:00&amp;lt;?, ?batch/s]
마스크 폴리곤화: 0geom [00:00, ?geom/s]
04_baseline 양성 픽셀 비율: 0.0603
04_baseline 예측 폴리곤: 492개
WARNING:tensorflow:From C:\Users\itwill\anaconda3\Lib\site-packages\keras\src\backend\common\global_state.py:82: The name tf.reset_default_graph is deprecated. Please use tf.compat.v1.reset_default_graph instead.

✓ 기존 모델 동일 영상 추론 완료: 308.7초 (5.15분) | RAM 1.64GB / 시스템 70%

▶ 파인튜닝 모델 동일 영상 추론 시작 | RAM 1.64GB / 시스템 70%
05_finetuned 전체 추론:   0%|          | 0/1540 [00:00&amp;lt;?, ?batch/s]
마스크 폴리곤화: 0geom [00:00, ?geom/s]
05_finetuned 양성 픽셀 비율: 0.0280
05_finetuned 예측 폴리곤: 1,612개
✓ 파인튜닝 모델 동일 영상 추론 완료: 360.2초 (6.00분) | RAM 1.69GB / 시스템 75%

▶ 기존 모델 대비 변화 영역 생성 시작 | RAM 1.69GB / 시스템 75%
마스크 폴리곤화: 0geom [00:00, ?geom/s]
06_added_after_finetune: 886,788픽셀, 1,211개 폴리곤
마스크 폴리곤화: 0geom [00:00, ?geom/s]
07_removed_after_finetune: 4,178,879픽셀, 669개 폴리곤
✓ 기존 모델 대비 변화 영역 생성 완료: 9.0초 (0.15분) | RAM 1.71GB / 시스템 76%

==============================================================================
완료
출력 폴더: D:\Final\02_Output\Jongro_WeakLabel_Finetune_2Tiles
==============================================================================&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1252&quot; data-origin-height=&quot;844&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxyhij/dJMcaf1LpVM/8ko31tFXZnVfXm7CyruIF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxyhij/dJMcaf1LpVM/8ko31tFXZnVfXm7CyruIF0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxyhij/dJMcaf1LpVM/8ko31tFXZnVfXm7CyruIF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbxyhij%2FdJMcaf1LpVM%2F8ko31tFXZnVfXm7CyruIF0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1252&quot; height=&quot;844&quot; data-origin-width=&quot;1252&quot; data-origin-height=&quot;844&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;▲ Skeletonization 기반 중심선을&amp;nbsp;이용한 학습/탐지 결과&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1048&quot; data-start=&quot;856&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;하지만 결과는 기대와 달랐다. 넓게 퍼지던 오탐은 줄었지만, 이번에는 &lt;b&gt;도로가 짧게 끊기거나 가느다란 선분처럼 파편화&lt;/b&gt;되어 나타났다. 특히 교차로나 넓은 도로에서는 실제 도로 폭을 제대로 표현하지 못했다.&lt;/p&gt;
&lt;p data-end=&quot;1291&quot; data-start=&quot;1056&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1291&quot; data-start=&quot;1056&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;(여러 방식을 종로구 전체에 반복 적용하기에는 시간이 너무 오래 걸렸기 때문에, 이 단계부터는 정사영상 TIFF 2개만 선정해 실험하였다. 먼저 작은 범위에서 라벨 방식이 실제 도로를 제대로 설명하는지 확인한 뒤, 효과가 검증된 방법만 전체 지역에 적용하려는 목적이었다. 무엇보다 이 모델은 이후 다른 자치구를 Fine-tuning하기 위한 기준 모델이므로, 종로구에서부터 도로의 형태를 안정적으로 설명할 수 있어야 했다.)&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;자동 생성된 라벨을 여러 차례 검토한 결과, 연구 목적에 부합하는 정답 데이터를 구축하기 위해서는 직접 라벨을 작성하는 것이 가장 적절하다고 판단하였다. 이에 따라 하나의 정사영상 TIFF 도엽을 선정하고, 영상에서 육안으로 통행 가능한 도로의 중심선을 직접 디지타이징하였다. 이후 수작업 중심선을 기준으로 약 2m의 Buffer를 생성하여, 연구 목적을 비교적 명확하게 반영한 고품질 정답 라벨로 사용하였다.&lt;/p&gt;
&lt;p data-end=&quot;499&quot; data-start=&quot;248&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;499&quot; data-start=&quot;248&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;표준노드링크 기반 학습에서 가장 우수한 성능을 보인 U-Net 모델을 초기 모델로 불러온 뒤, 수작업 라벨이 구축된 정사영상을 이용하여 Fine-tuning을 수행하였다. 이후 모델의 성능을 두 가지 관점에서 확인하였다. 먼저 학습 라벨을 구축한 동일한 정사영상에서 수작업 도로를 얼마나 충실하게 재현하는지 확인하고, 다음으로 학습에 사용하지 않은 종로구 내 다른 정사영상에 적용하여 새로운 공간에서도 도로의 특징을 탐지할 수 있는지 검토하였다.&lt;/p&gt;
&lt;p data-end=&quot;735&quot; data-start=&quot;507&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;735&quot; data-start=&quot;507&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이를 위해 총 2개의 정사영상 TIFF 도엽을 사용하였다. 첫 번째 도엽은 수작업 라벨 구축과 Fine-tuning에 사용한 학습 대상 영상이며, 두 번째 도엽은 동일한 자치구에 위치하지만 추가 학습에는 사용하지 않은 외부 검증용 영상이다. 이를 통해 모델이 학습 영상을 단순히 기억한 것인지, 아니면 동일 지역의 다른 영상에도 적용 가능한 도로의 시각적 특징을 학습한 것인지를 정성적으로 비교하고자 하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;578&quot; data-origin-height=&quot;723&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9xBaw/dJMcafU52dS/WdrWnLmODzYmzVkIvvQvq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9xBaw/dJMcafU52dS/WdrWnLmODzYmzVkIvvQvq1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9xBaw/dJMcafU52dS/WdrWnLmODzYmzVkIvvQvq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9xBaw%2FdJMcafU52dS%2FWdrWnLmODzYmzVkIvvQvq1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;578&quot; height=&quot;723&quot; data-origin-width=&quot;578&quot; data-origin-height=&quot;723&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;▲ 디지타이징한 도로 중심선&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도로탐지결과 (펼치기) ▼&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1785485514794&quot; class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;================================================================================
종로구 전체 정사영상 도로 탐지
추가 학습 없음 / 타일별 정규화 추론
================================================================================

모델: D:\Final\02_Output\Jongro_Digit_StrongLabel_Finetune_B060\03_digit_stronglabel_finetuned_best.keras
입력 폴더: D:\Final\00_Raw\06_map_25\01_jongro\georeferenced
TIFF 수: 12
출력 폴더: D:\Final\02_Output\Jongro_Digit_Model_Full_Test_PerTileNorm
임계값: 0.40
최소 폴리곤 면적: 8.00 m&amp;sup2;

[1/4] 베스트모델 로드
  모델: D:\Final\02_Output\Jongro_Digit_StrongLabel_Finetune_B060\03_digit_stronglabel_finetuned_best.keras
  입력 형태: (None, 256, 256, 3)
  출력 형태: (None, 256, 256, 1)

[2/4] 종로구 전체 TIFF 순차 추론

[1/12] (B060)정사영상_2025_37608048_EPSG5179.tif
  LOW RGB : [12. 31. 25.]
  HIGH RGB: [160. 169. 154.]
  완료: (B060)정사영상_2025_37608048_EPSG5179.tif | 도로 픽셀 비율 3.15% | 폴리곤 615개                    

[2/12] (B060)정사영상_2025_37608049_EPSG5179.tif
  LOW RGB : [ 7. 19. 16.]
  HIGH RGB: [119. 125. 102.]
  완료: (B060)정사영상_2025_37608049_EPSG5179.tif | 도로 픽셀 비율 4.14% | 폴리곤 2,955개                  

[3/12] (B060)정사영상_2025_37608050_EPSG5179.tif
  LOW RGB : [13. 28. 22.]
  HIGH RGB: [86. 91. 71.]
  완료: (B060)정사영상_2025_37608050_EPSG5179.tif | 도로 픽셀 비율 1.61% | 폴리곤 670개                    

[4/12] (B060)정사영상_2025_37608058_EPSG5179.tif
  LOW RGB : [ 4. 21. 19.]
  HIGH RGB: [170. 182. 172.]
  완료: (B060)정사영상_2025_37608058_EPSG5179.tif | 도로 픽셀 비율 5.65% | 폴리곤 1,058개                  

[5/12] (B060)정사영상_2025_37608059_EPSG5179.tif
  LOW RGB : [ 6. 20. 17.]
  HIGH RGB: [146. 159. 145.]
  완료: (B060)정사영상_2025_37608059_EPSG5179.tif | 도로 픽셀 비율 4.55% | 폴리곤 758개                    

[6/12] (B060)정사영상_2025_37608060_EPSG5179.tif
  LOW RGB : [ 7. 20. 17.]
  HIGH RGB: [136. 151. 136.]
  완료: (B060)정사영상_2025_37608060_EPSG5179.tif | 도로 픽셀 비율 2.87% | 폴리곤 689개                    

[7/12] (B060)정사영상_2025_37608069_EPSG5179.tif
  LOW RGB : [ 4. 14. 11.]
  HIGH RGB: [157. 168. 152.]
  완료: (B060)정사영상_2025_37608069_EPSG5179.tif | 도로 픽셀 비율 5.78% | 폴리곤 1,140개                  

[8/12] (B060)정사영상_2025_37608070_EPSG5179.tif
  LOW RGB : [ 7. 20. 14.]
  HIGH RGB: [176. 181. 165.]
  완료: (B060)정사영상_2025_37608070_EPSG5179.tif | 도로 픽셀 비율 6.63% | 폴리곤 1,032개                  

[9/12] (B060)정사영상_2025_37608079_EPSG5179.tif
  LOW RGB : [26. 52. 42.]
  HIGH RGB: [192. 196. 181.]
  완료: (B060)정사영상_2025_37608079_EPSG5179.tif | 도로 픽셀 비율 4.96% | 폴리곤 2,568개                  

[10/12] (B060)정사영상_2025_37608080_EPSG5179.tif
  LOW RGB : [15. 39. 27.]
  HIGH RGB: [187. 193. 179.]
  완료: (B060)정사영상_2025_37608080_EPSG5179.tif | 도로 픽셀 비율 5.54% | 폴리곤 2,636개                  

[11/12] (B060)정사영상_2025_37705061_EPSG5179.tif
  LOW RGB : [ 8. 29. 29.]
  HIGH RGB: [184. 195. 185.]
  완료: (B060)정사영상_2025_37705061_EPSG5179.tif | 도로 픽셀 비율 7.44% | 폴리곤 2,290개                  

[12/12] (B060)정사영상_2025_37705071_EPSG5179.tif
  LOW RGB : [18. 43. 38.]
  HIGH RGB: [191. 196. 184.]
  완료: (B060)정사영상_2025_37705071_EPSG5179.tif | 도로 픽셀 비율 5.33% | 폴리곤 2,980개                  

[3/4] 타일별 도로 폴리곤 병합
폴리곤 읽기: 100%|██████████| 12/12 [00:01&amp;lt;00:00,  7.75it/s]
  통합 폴리곤 수: 19,391
  저장: D:\Final\02_Output\Jongro_Digit_Model_Full_Test_PerTileNorm\Jongro_all_predicted_roads.gpkg

[4/4] 처리 요약
  전체 TIFF: 12
  폴리곤 생성 타일: 12
  실패 타일: 0
  요약: D:\Final\02_Output\Jongro_Digit_Model_Full_Test_PerTileNorm\processing_summary.txt
  전체 통합 GPKG: D:\Final\02_Output\Jongro_Digit_Model_Full_Test_PerTileNorm\Jongro_all_predicted_roads.gpkg

================================================================================
완료
================================================================================

출력 구조
normalization\ : 타일별 RGB 정규화 기록
probability\   : 타일별 도로 확률 TIFF
binary_mask\   : 타일별 이진 마스크 TIFF
polygon\       : 타일별 도로 폴리곤 GPKG
Jongro_all_predicted_roads.gpkg
                : 종로구 전체 통합 폴리곤&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1060&quot; data-origin-height=&quot;858&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cYIoxC/dJMcadv21iC/xAVO4N3vHQjKZQgTDARps1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cYIoxC/dJMcadv21iC/xAVO4N3vHQjKZQgTDARps1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cYIoxC/dJMcadv21iC/xAVO4N3vHQjKZQgTDARps1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcYIoxC%2FdJMcadv21iC%2FxAVO4N3vHQjKZQgTDARps1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1060&quot; height=&quot;858&quot; data-origin-width=&quot;1060&quot; data-origin-height=&quot;858&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;▲ 디지타이징한 도로 중심선 + 2m buffer를 사용한 학습/탐지 결과&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;349&quot; data-start=&quot;146&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;수작업으로 디지타이징한 도로 중심선과 2m Buffer를 이용해 Fine-tuning한 결과, 앞선 실험들보다 생활도로와 골목길의 연속성이 뚜렷하게 개선되었다. 특히 학습에 사용하지 않은 동일 자치구 내 다른 정사영상에서도 주요 도로뿐 아니라 일부 협소도로까지 비교적 잘 추출되어, 수작업 라벨이 모델의 공간적 일반화에 일정 부분 기여했음을 확인할 수 있었다.&lt;/p&gt;
&lt;p data-end=&quot;506&quot; data-start=&quot;357&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;506&quot; data-start=&quot;357&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그러나 예측 결과에는 여전히 상당한 노이즈가 남아 있었다. &lt;b&gt;건물 경계, 옥상, 주차장, 그림자, 밝은 포장면과 같이 도로와 유사한 시각적 특징을 가진 영역이 부분적으로 도로로 오분류&lt;/b&gt;되었고, &lt;b&gt;일부 구간에서는 짧은 선분이나 작은 폐곡선 형태의 예측이 다수 발생&lt;/b&gt;하였다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-end=&quot;774&quot; data-start=&quot;514&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;앞서 수작업으로 구축한 중심선 라벨을 이용한 Fine-tuning에서도 생활도로의 탐지 성능은 개선되었지만, 중심선을 기준으로 동일한 2m Buffer를 적용하면서 도로의 실제 폭을 충분히 반영하지 못하는 문제가 남아 있었다. 광폭도로는 실제 도로의 일부 영역만 정답으로 학습되는 반면, 협소한 골목길에서는 Buffer가 건물 가장자리 등 비도로 영역까지 포함하기도 하였다. 이에 중심선이 아닌 &lt;b&gt;실제 도로 경계를 Polygon 형태로 직접 디지타이징하여 정답 데이터를 다시 구축&lt;/b&gt;하기로 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;다만 수작업 Polygon 구축에는 상당한 시간이 소요되므로, 이번 단계에서는 종로구 전체가 아닌 &lt;b&gt;정사영상 TIFF 1개 도엽을 대상으로 고품질 정답 데이터를 구축&lt;/b&gt;하였다. 이는 종로구 전체를 대표하는 학습데이터를 완성하기 위한 단계라기보다, 정확하게 구축한 소규모 정답 데이터가 기존 모델의 탐지 결과를 개선할 수 있는지 우선 확인하기 위한 실험이다. 이후 해당 데이터를 이용해 Fine-tuning을 진행하고, 학습에 사용하지 않은 다른 정사영상에서도 개선 효과가 나타나는지 확인하고자 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;980&quot; data-origin-height=&quot;1364&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sjBVd/dJMcadiKCDd/0fQ7mXyrWR7tBVYfK3LP0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sjBVd/dJMcadiKCDd/0fQ7mXyrWR7tBVYfK3LP0K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sjBVd/dJMcadiKCDd/0fQ7mXyrWR7tBVYfK3LP0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsjBVd%2FdJMcadiKCDd%2F0fQ7mXyrWR7tBVYfK3LP0K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;643&quot; height=&quot;895&quot; data-origin-width=&quot;980&quot; data-origin-height=&quot;1364&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;▲ 디지타이징한 도로&lt;/p&gt;</description>
      <category>개인 프로젝트/골목길 탐지하기</category>
      <author>ecosso</author>
      <guid isPermaLink="true">https://ecosso.tistory.com/150</guid>
      <comments>https://ecosso.tistory.com/150#entry150comment</comments>
      <pubDate>Fri, 31 Jul 2026 17:32:30 +0900</pubDate>
    </item>
    <item>
      <title>#5 5일차_감성 분석 (나이브베이즈, RNN)</title>
      <link>https://ecosso.tistory.com/148</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;01 쇼핑몰&amp;nbsp;후기&amp;nbsp;감성&amp;nbsp;분석&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; 02 참고 : 최신 java 설치 &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;03&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;쇼핑몰&amp;nbsp;후기&amp;nbsp;감성&amp;nbsp;분석&amp;nbsp;-&amp;nbsp;모델링(나이브베이즈)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;04&lt;span&gt;&amp;nbsp;&lt;/span&gt;쇼핑몰&amp;nbsp;후기&amp;nbsp;감성&amp;nbsp;분석&amp;nbsp;-&amp;nbsp;모델링(RNN)&lt;/b&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;01&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;쇼핑몰&amp;nbsp;후기&amp;nbsp;감성&amp;nbsp;분석 &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;1.&amp;nbsp;데이터&amp;nbsp;로딩 &lt;br /&gt;import&amp;nbsp;pandas&amp;nbsp;as&amp;nbsp;pd &lt;br /&gt;df&amp;nbsp;=&amp;nbsp;pd.read_table('shopping_ratings_total.txt',&amp;nbsp;names&amp;nbsp;=&amp;nbsp;['ratings',&amp;nbsp;'reviews']) &lt;br /&gt;&lt;br /&gt;df.head() &lt;br /&gt;df.shape&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;(200000,&amp;nbsp;2) &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;2.&amp;nbsp;target&amp;nbsp;변수&amp;nbsp;변환(평점&amp;nbsp;-&amp;gt;&amp;nbsp;이진데이터) &lt;br /&gt;import&amp;nbsp;numpy&amp;nbsp;as&amp;nbsp;np &lt;br /&gt;np.unique(df['ratings'])&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;[1,&amp;nbsp;2,&amp;nbsp;4,&amp;nbsp;5] &lt;br /&gt;&lt;br /&gt;df['ratings']&amp;nbsp;=&amp;nbsp;np.where(df['ratings']&amp;nbsp;&amp;lt;=&amp;nbsp;2,&amp;nbsp;0,&amp;nbsp;1)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;평점&amp;nbsp;1,2&amp;nbsp;-&amp;gt;&amp;nbsp;0(부정)&amp;nbsp; &lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 평점 4,5 -&amp;gt; 1(긍정)&amp;nbsp; &lt;br /&gt;df['ratings'].value_counts() &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#&amp;nbsp;3.&amp;nbsp;샘플링(실제&amp;nbsp;분석과정에서는&amp;nbsp;생략) &lt;br /&gt;#&amp;nbsp;20만건&amp;nbsp;학습&amp;nbsp;불가&amp;nbsp;-&amp;gt;&amp;nbsp;1만건만&amp;nbsp;랜덤&amp;nbsp;샘플링해서&amp;nbsp;진행 &lt;br /&gt;df&amp;nbsp;=&amp;nbsp;df.sample(n=10000,&amp;nbsp;random_state=0).reset_index(drop=True)&amp;nbsp;&amp;nbsp;#&amp;nbsp;랜덤&amp;nbsp;샘플링&amp;nbsp;-&amp;gt;&amp;nbsp;index&amp;nbsp;초기화&amp;nbsp; &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;4.&amp;nbsp;데이터&amp;nbsp;분리(train/test)&amp;nbsp; &lt;br /&gt;from&amp;nbsp;sklearn.model_selection&amp;nbsp;import&amp;nbsp;train_test_split &lt;br /&gt;train_x,&amp;nbsp;test_x,&amp;nbsp;train_y,&amp;nbsp;test_y&amp;nbsp;=&amp;nbsp;train_test_split(df['reviews'],&amp;nbsp;df['ratings'],&amp;nbsp;random_state=0,&amp;nbsp;stratify=df['ratings']) &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;5.&amp;nbsp;전처리(정제&amp;nbsp;-&amp;gt;&amp;nbsp;형태소&amp;nbsp;분석) &lt;br /&gt;#&amp;nbsp;pip&amp;nbsp;install&amp;nbsp;konlpy &lt;br /&gt;import&amp;nbsp;konlpy.tag &lt;br /&gt;Okt&amp;nbsp;=&amp;nbsp;konlpy.tag.Okt()&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;RuntimeError:&amp;nbsp;Java&amp;nbsp;version&amp;nbsp;too&amp;nbsp;old.&amp;nbsp;Java&amp;nbsp;9&amp;nbsp;or&amp;nbsp;later&amp;nbsp;is&amp;nbsp;required &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;cmd에서&amp;nbsp;java&amp;nbsp;-version &lt;br /&gt;#&amp;nbsp;sample&amp;nbsp;리뷰로&amp;nbsp;형태소&amp;nbsp;분석 &lt;br /&gt;sample&amp;nbsp;=&amp;nbsp;train_x.values[4] &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;1)&amp;nbsp;정제&amp;nbsp;:&amp;nbsp;불필요한&amp;nbsp;글자&amp;nbsp;제거&amp;nbsp;-&amp;gt;&amp;nbsp;영어,&amp;nbsp;숫자,&amp;nbsp;특수기호&amp;nbsp;등 &lt;br /&gt;from&amp;nbsp;pandas&amp;nbsp;import&amp;nbsp;Series &lt;br /&gt;Series(sample).replace('[^가-힣]','',regex=True)&lt;/p&gt;
&lt;pre id=&quot;code_1785287250076&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Series(sample).replace('[^가-힣]','',regex=True)
Out[21]: 
0    살키로남자아이구매했어요타이트하게잘맞아요무엇보다소재가너무좋네요세탁후에도보풀이안나고신축...
dtype: object&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;내&amp;nbsp;컴퓨터에서&amp;nbsp;오류가&amp;nbsp;계속&amp;nbsp;발생해서&amp;nbsp;아래&amp;nbsp;코드로&amp;nbsp;진행하였음 &lt;br /&gt;import&amp;nbsp;konlpy.tag &lt;br /&gt;&lt;br /&gt;Okt&amp;nbsp;=&amp;nbsp;konlpy.tag.Okt() &lt;br /&gt;&lt;br /&gt;sample_clean&amp;nbsp;=&amp;nbsp;re.sub(r'[^가-힣\s]',&amp;nbsp;'',&amp;nbsp;sample) &lt;br /&gt;sample_token&amp;nbsp;=&amp;nbsp;Okt.morphs(sample_clean) &lt;br /&gt;&lt;br /&gt;print(sample_token)&lt;/p&gt;
&lt;pre id=&quot;code_1785287674188&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import konlpy.tag

Okt = konlpy.tag.Okt()

sample_clean = re.sub(r'[^가-힣\s]', '', sample)
sample_token = Okt.morphs(sample_clean)

print(sample_token)
['살', '키로', '남자', '아이', '구매', '했어요', '타이', '트', '하', '게', '잘', '맞아요', '무엇', '보다', '소재', '가', '너무', '좋네요', '세탁', '후', '에도', '보풀', '이', '안나', '고', '신축', '성도', '좋습니다', '기모', '라', '축구', '할', '때', '땀', '흘리며', '열심히', '뛰며', '공차', '네', '요']&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;3)&amp;nbsp;어간추출 &lt;br /&gt;#&amp;nbsp;먹었습니다&amp;nbsp;=&amp;gt;&amp;nbsp;먹(어간)&amp;nbsp;+&amp;nbsp;었&amp;nbsp;+&amp;nbsp;습니다 &lt;br /&gt;stem_result&amp;nbsp;=&amp;nbsp;Okt.morphs(sample_clean, stem=True) # 토큰화 + 어간추출 동시 진행&lt;/p&gt;
&lt;pre id=&quot;code_1785287716973&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Okt.morphs(sample_clean, stem=True)
Out[48]: 
['살',
 '키로',
 '남자',
 '아이',
 '구매',
 '하다',
 '타이',
 '트',
 '하',
 '게',
 '자다',
 '맞다',
 '무엇',
 '보다',
 '소재',
 '가',
 '너무',
 '좋다',
 '세탁',
 '후',
 '에도',
 '보풀',
 '이',
 '안나',
 '고',
 '신축',
 '성도',
 '좋다',
 '기모',
 '라',
 '축구',
 '하다',
 '때',
 '땀',
 '흘리다',
 '열심히',
 '뛰다',
 '공차',
 '네',
 '요']&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;4)&amp;nbsp;품사태깅&amp;nbsp;:&amp;nbsp;품사(동사/명사/형용사&amp;nbsp;등)&amp;nbsp;확인&amp;nbsp;과정&amp;nbsp;=&amp;gt;&amp;nbsp;조사를&amp;nbsp;제거(불용어&amp;nbsp;제거) &lt;br /&gt;#&amp;nbsp;불용어를&amp;nbsp;태그한&amp;nbsp;뒤&amp;nbsp;삭제하기&amp;nbsp;위해서&amp;nbsp;진행하는&amp;nbsp;과정이다. &lt;br /&gt;#&amp;nbsp;(명사&amp;nbsp;:&amp;nbsp;Noun,&amp;nbsp;조사&amp;nbsp;:&amp;nbsp;Josa,&amp;nbsp;형용사&amp;nbsp;:&amp;nbsp;Adjective,&amp;nbsp;부사&amp;nbsp;:&amp;nbsp;Adverb&amp;nbsp;등) &lt;br /&gt;pos_result&amp;nbsp;=&amp;nbsp;Okt.pos(sample_clean)&lt;/p&gt;
&lt;pre id=&quot;code_1785287991021&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Okt.pos(sample_clean)
Out[50]: 
[('살', 'Noun'),
 ('키로', 'Suffix'),
 ('남자', 'Noun'),
 ('아이', 'Noun'),
 ('구매', 'Noun'),
 ('했어요', 'Verb'),
 ('타이', 'Noun'),
 ('트', 'Noun'),
 ('하', 'Suffix'),
 ('게', 'Josa'),
 ('잘', 'Verb'),
 ('맞아요', 'Verb'),
 ('무엇', 'Noun'),
 ('보다', 'Josa'),
 ('소재', 'Noun'),
 ('가', 'Josa'),
 ('너무', 'Adverb'),
 ('좋네요', 'Adjective'),
 ('세탁', 'Noun'),
 ('후', 'Noun'),
 ('에도', 'Josa'),
 ('보풀', 'Noun'),
 ('이', 'Josa'),
 ('안나', 'Noun'),
 ('고', 'Josa'),
 ('신축', 'Noun'),
 ('성도', 'Noun'),
 ('좋습니다', 'Adjective'),
 ('기모', 'Noun'),
 ('라', 'Josa'),
 ('축구', 'Noun'),
 ('할', 'Verb'),
 ('때', 'Noun'),
 ('땀', 'Noun'),
 ('흘리며', 'Verb'),
 ('열심히', 'Adverb'),
 ('뛰며', 'Verb'),
 ('공차', 'Noun'),
 ('네', 'Suffix'),
 ('요', 'Josa')]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 조사 사전을 만들기 위한 작업&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[&amp;nbsp;word&amp;nbsp;for&amp;nbsp;word,&amp;nbsp;pos&amp;nbsp;in&amp;nbsp;pos_result&amp;nbsp;if&amp;nbsp;pos&amp;nbsp;==&amp;nbsp;'Josa'&amp;nbsp;]&lt;/p&gt;
&lt;pre id=&quot;code_1785289080277&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[ word for word, pos in pos_result if pos == 'Josa' ]
Out[55]: ['게', '보다', '가', '에도', '이', '고', '라', '요']&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;***&amp;nbsp;전체&amp;nbsp;리뷰(train)에&amp;nbsp;대한&amp;nbsp;형태소&amp;nbsp;분석 &lt;br /&gt;Josa&amp;nbsp;=&amp;nbsp;[]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;각&amp;nbsp;문장마다의&amp;nbsp;조사를&amp;nbsp;누적 &lt;br /&gt;stops&amp;nbsp;=&amp;nbsp;[]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;각&amp;nbsp;문장마다의&amp;nbsp;불용어&amp;nbsp;누적 &lt;br /&gt;train_token&amp;nbsp;=&amp;nbsp;[]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;각&amp;nbsp;문장마다의&amp;nbsp;토큰화(어간추출&amp;nbsp;적용)된&amp;nbsp;단어&amp;nbsp;누적 &lt;br /&gt;&lt;br /&gt;for&amp;nbsp;vtext&amp;nbsp;in&amp;nbsp;train_x.values&amp;nbsp;: &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;vtext&amp;nbsp;=&amp;nbsp;Series(vtext).replace('[^가-힣&amp;nbsp;]',&amp;nbsp;'',&amp;nbsp;regex=True)[0]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;정제작업(한글과&amp;nbsp;공백이&amp;nbsp;아닌&amp;nbsp;모든&amp;nbsp;글자&amp;nbsp;제거) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;조사&amp;nbsp;수집(각&amp;nbsp;문장마다&amp;nbsp;조사를&amp;nbsp;수집) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pos_result&amp;nbsp;=&amp;nbsp;Okt.pos(vtext)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;품사태깅 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Josa&amp;nbsp;=&amp;nbsp;Josa&amp;nbsp;+&amp;nbsp;[&amp;nbsp;word&amp;nbsp;for&amp;nbsp;word,&amp;nbsp;pos&amp;nbsp;in&amp;nbsp;pos_result&amp;nbsp;if&amp;nbsp;pos&amp;nbsp;==&amp;nbsp;'Josa']&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;조사사전&amp;nbsp;생성 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;토큰화&amp;nbsp;+&amp;nbsp;어간추출 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;vtext&amp;nbsp;=&amp;nbsp;Okt.morphs(vtext,&amp;nbsp;stem=True)&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;불용어&amp;nbsp;사전&amp;nbsp;:&amp;nbsp;조사&amp;nbsp;+&amp;nbsp;한글자&amp;nbsp;글자&amp;nbsp;+&amp;nbsp;빈도수&amp;nbsp;1인&amp;nbsp;단어 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;stops&amp;nbsp;=&amp;nbsp;stops&amp;nbsp;+&amp;nbsp;[word&amp;nbsp;for&amp;nbsp;word&amp;nbsp;in&amp;nbsp;vtext&amp;nbsp;if&amp;nbsp;len(word)&amp;nbsp;==&amp;nbsp;1]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;토큰화&amp;nbsp;된&amp;nbsp;단어&amp;nbsp;중&amp;nbsp;길이가&amp;nbsp;1인&amp;nbsp;단어&amp;nbsp;고르기 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;토큰화&amp;nbsp;된&amp;nbsp;단어&amp;nbsp;저장(각&amp;nbsp;문장마다&amp;nbsp;수집&amp;nbsp;필요) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;train_token.append(vtext) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;결과&amp;nbsp;확인 &lt;br /&gt;stops &lt;br /&gt;Josa &lt;br /&gt;train_token &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#&amp;nbsp;4.&amp;nbsp;불용어&amp;nbsp;제거 &lt;br /&gt;#&amp;nbsp;1)&amp;nbsp;불용어&amp;nbsp;사전&amp;nbsp;생성&amp;nbsp;:&amp;nbsp;조사&amp;nbsp;+&amp;nbsp;한글자&amp;nbsp;단어&amp;nbsp;+&amp;nbsp;빈도수&amp;nbsp;1개인&amp;nbsp;단어 &lt;br /&gt;import&amp;nbsp;numpy&amp;nbsp;as&amp;nbsp;np &lt;br /&gt;all_train_words&amp;nbsp;=&amp;nbsp;[w&amp;nbsp;for&amp;nbsp;token&amp;nbsp;in&amp;nbsp;train_token&amp;nbsp;for&amp;nbsp;w&amp;nbsp;in&amp;nbsp;token&amp;nbsp;]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;모든&amp;nbsp;문장의&amp;nbsp;토큰화&amp;nbsp;결과&amp;nbsp;합치기 &lt;br /&gt;freq1_words&amp;nbsp;=&amp;nbsp;[word&amp;nbsp;for&amp;nbsp;word&amp;nbsp;in&amp;nbsp;all_train_words&amp;nbsp;if&amp;nbsp;all_train_words.count(word)&amp;nbsp;==&amp;nbsp;1]&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;모든&amp;nbsp;문장에서&amp;nbsp;빈도수가&amp;nbsp;1인&amp;nbsp;단어만&amp;nbsp;골라내기 &lt;br /&gt;&lt;br /&gt;stops&amp;nbsp;=&amp;nbsp;np.unique(Josa&amp;nbsp;+&amp;nbsp;stops&amp;nbsp;+&amp;nbsp;freq1_words)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;불용어&amp;nbsp;사전&amp;nbsp;생성 &lt;br /&gt;len(stops)&lt;/p&gt;
&lt;pre id=&quot;code_1785292140982&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;len(stops)
Out[71]: 15&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;2)&amp;nbsp;각&amp;nbsp;토큰화&amp;nbsp;된&amp;nbsp;결과에서&amp;nbsp;불용어&amp;nbsp;제거(train/test&amp;nbsp;각각&amp;nbsp;제거) &lt;br /&gt;from&amp;nbsp;numpy&amp;nbsp;import&amp;nbsp;nan&amp;nbsp;as&amp;nbsp;NA &lt;br /&gt;&lt;br /&gt;def&amp;nbsp;token_clean_func(text,&amp;nbsp;stops)&amp;nbsp;: &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;result&amp;nbsp;=&amp;nbsp;[]&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;vtext&amp;nbsp;in&amp;nbsp;text.values&amp;nbsp;: &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;vtext&amp;nbsp;=&amp;nbsp;Okt.morphs(vtext,&amp;nbsp;stem=True)&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;vtext&amp;nbsp;=&amp;nbsp;Series(vtext).replace('[^가-힣&amp;nbsp;]',&amp;nbsp;'',&amp;nbsp;regex=True)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;vtext_rm&amp;nbsp;=&amp;nbsp;[word&amp;nbsp;for&amp;nbsp;word&amp;nbsp;in&amp;nbsp;vtext&amp;nbsp;if&amp;nbsp;word&amp;nbsp;not&amp;nbsp;in&amp;nbsp;stops]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;불용어&amp;nbsp;제거 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;vtext_rm&amp;nbsp;=&amp;nbsp;Series(vtext_rm).replace('',NA).dropna().to_list()&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;빈문자열&amp;nbsp;제거 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;result.append(vtext_rm) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;result &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;train_token&amp;nbsp;=&amp;nbsp;token_clean_func(train_x,&amp;nbsp;stops) &lt;br /&gt;test_token&amp;nbsp;=&amp;nbsp;token_clean_func(test_x,&amp;nbsp;stops)&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;02 참고 : 최신 java 설치&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;1)&amp;nbsp;프로그램&amp;nbsp;다운로드&amp;nbsp;/&amp;nbsp;설치 &lt;br /&gt;#&amp;nbsp;&lt;a href=&quot;https://www.oracle.com/java/technologies/downloads/#jdk26-windows&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.oracle.com/java/technologies/downloads/#jdk26-windows&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;#&amp;nbsp;2)&amp;nbsp;시스템&amp;nbsp;PATH&amp;nbsp;등록 &lt;br /&gt;#&amp;nbsp;검색창&amp;nbsp;&amp;gt;&amp;nbsp;시스템&amp;nbsp;환경&amp;nbsp;변수&amp;nbsp;&amp;gt;&amp;nbsp;PATH&amp;nbsp;등록 &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;3)&amp;nbsp;파이썬&amp;nbsp;PATH&amp;nbsp;등록 &lt;br /&gt;import&amp;nbsp;os &lt;br /&gt;os.environ['JAVA_HOME']&amp;nbsp;=&amp;nbsp;r'C:\Program&amp;nbsp;Files\Java\jdk-26.0.2' &lt;br /&gt;print(os.environ.get('JAVA_HOME'))&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;03 쇼핑몰&amp;nbsp;후기&amp;nbsp;감성&amp;nbsp;분석&amp;nbsp;-&amp;nbsp;모델링(나이브베이즈)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;나이브베이즈&amp;nbsp;모델 &lt;br /&gt;-&amp;nbsp;단어&amp;nbsp;기반으로&amp;nbsp;사후&amp;nbsp;확률을&amp;nbsp;계산 &lt;br /&gt;-&amp;nbsp;각&amp;nbsp;문장에서&amp;nbsp;각&amp;nbsp;단어가&amp;nbsp;서로&amp;nbsp;독립적이라고&amp;nbsp;가정&amp;nbsp;(실제로는&amp;nbsp;가정&amp;nbsp;성립&amp;nbsp;자체가&amp;nbsp;어려움)&amp;nbsp;=&amp;gt;&amp;nbsp;문맥&amp;nbsp;파악&amp;nbsp;어려움 &lt;br /&gt;-&amp;nbsp;TF-IDF&amp;nbsp;변환으로&amp;nbsp;문장을&amp;nbsp;벡터화&amp;nbsp;-&amp;gt;&amp;nbsp;각&amp;nbsp;문장을&amp;nbsp;단어&amp;nbsp;빈도수&amp;nbsp;기반으로&amp;nbsp;숫자로&amp;nbsp;변경 &lt;br /&gt;&lt;br /&gt;**&amp;nbsp;TF-IDF&amp;nbsp;변환 &lt;br /&gt;1)&amp;nbsp;TF(Term&amp;nbsp;Frequency)&amp;nbsp;변환 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;단어의&amp;nbsp;빈도수에&amp;nbsp;가중치를&amp;nbsp;두고&amp;nbsp;각&amp;nbsp;단어가&amp;nbsp;얼마나&amp;nbsp;중요한지를&amp;nbsp;수치로&amp;nbsp;나타내는&amp;nbsp;작업 &lt;br /&gt;&lt;br /&gt;2)&amp;nbsp;IDF(Inverse&amp;nbsp;Document&amp;nbsp;Frequency)&amp;nbsp;변환 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;단어가&amp;nbsp;문서&amp;nbsp;전체에서&amp;nbsp;얼마나&amp;nbsp;희귀한가를&amp;nbsp;나타내는&amp;nbsp;작업 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;#&amp;nbsp;1.&amp;nbsp;데이터&amp;nbsp;로딩 &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;2.&amp;nbsp;전처리 &lt;br /&gt;#&amp;nbsp;1)&amp;nbsp;정제 &lt;br /&gt;#&amp;nbsp;2)&amp;nbsp;토큰화&amp;nbsp;+&amp;nbsp;어간추출 &lt;br /&gt;#&amp;nbsp;3)&amp;nbsp;불용어&amp;nbsp;제거 &lt;br /&gt;&lt;br /&gt;len(train_token) &lt;br /&gt;len(test_token)&lt;/p&gt;
&lt;pre id=&quot;code_1785294005607&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;len(train_token)
Out[75]: 7500

len(test_token)
Out[76]: 2500&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;3.&amp;nbsp;TF-IDF&amp;nbsp;변환 &lt;br /&gt;#&amp;nbsp;1)&amp;nbsp;토큰화&amp;nbsp;-&amp;gt;&amp;nbsp;문장 &lt;br /&gt;'&amp;nbsp;'.join(train_token[0]) &lt;br /&gt;&lt;br /&gt;train_join&amp;nbsp;=&amp;nbsp;['&amp;nbsp;'.join(token_list)&amp;nbsp;for&amp;nbsp;token_list&amp;nbsp;in&amp;nbsp;train_token] &lt;br /&gt;test_join&amp;nbsp;=&amp;nbsp;['&amp;nbsp;'.join(token_list)&amp;nbsp;for&amp;nbsp;token_list&amp;nbsp;in&amp;nbsp;test_token] &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;2)&amp;nbsp;TF-IDF&amp;nbsp;변환(불용어&amp;nbsp;제거&amp;nbsp;후&amp;nbsp;문장으로&amp;nbsp;변환하여&amp;nbsp;입력) &lt;br /&gt;from&amp;nbsp;sklearn.feature_extraction.text&amp;nbsp;import&amp;nbsp;TfidfVectorizer &lt;br /&gt;m_tfidf&amp;nbsp;=&amp;nbsp;TfidfVectorizer() &lt;br /&gt;train_x_tfidf&amp;nbsp;=&amp;nbsp;m_tfidf.fit_transform(train_join) &lt;br /&gt;test_x_tfidf&amp;nbsp;&amp;nbsp;=&amp;nbsp;m_tfidf.transform(test_join) &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;4.&amp;nbsp;모델링&amp;nbsp;/&amp;nbsp;평가 &lt;br /&gt;import&amp;nbsp;sklearn.naive_bayes &lt;br /&gt;from&amp;nbsp;sklearn.naive_bayes&amp;nbsp;import&amp;nbsp;MultinomialNB,&amp;nbsp;CategoricalNB,&amp;nbsp;BernoulliNB &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;-&amp;nbsp;MultinomialNB&amp;nbsp;:&amp;nbsp;카운트&amp;nbsp;데이터에&amp;nbsp;적합 &lt;br /&gt;#&amp;nbsp;-&amp;nbsp;BernoulliNB&amp;nbsp;:&amp;nbsp;이진&amp;nbsp;데이터&amp;nbsp;분류&amp;nbsp;적합 &lt;br /&gt;#&amp;nbsp;-&amp;nbsp;CategoricalNB&amp;nbsp;:&amp;nbsp;다중&amp;nbsp;분류&amp;nbsp;적합 &lt;br /&gt;&lt;br /&gt;model&amp;nbsp;=&amp;nbsp;MultinomialNB() &lt;br /&gt;model.fit(train_x_tfidf,&amp;nbsp;train_y) &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;평가 &lt;br /&gt;model.score(train_x_tfidf,&amp;nbsp;train_y)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;0.8952 &lt;br /&gt;model.score(test_x_tfidf,&amp;nbsp;test_y)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;0.8588 &lt;br /&gt;&lt;br /&gt;model.predict_proba(test_x_tfidf[0]) &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;5.&amp;nbsp;리뷰&amp;nbsp;테스트 &lt;br /&gt;test&amp;nbsp;=&amp;nbsp;'배송도&amp;nbsp;빠르고&amp;nbsp;실물이&amp;nbsp;너무&amp;nbsp;예뻐요&amp;nbsp;대만족입니다' &lt;br /&gt;def&amp;nbsp;f_review(text,&amp;nbsp;model)&amp;nbsp;: &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;vtext&amp;nbsp;=&amp;nbsp;Series(text).replace('[^가-힣]','',regex=True)[0] &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;vtest&amp;nbsp;=&amp;nbsp;Okt.morphs(vtext,&amp;nbsp;stem=True) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;vtext&amp;nbsp;=&amp;nbsp;[word&amp;nbsp;for&amp;nbsp;word&amp;nbsp;in&amp;nbsp;vtext&amp;nbsp;if&amp;nbsp;word&amp;nbsp;not&amp;nbsp;in&amp;nbsp;stops] &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;vtext&amp;nbsp;=&amp;nbsp;'&amp;nbsp;'.join(vtext) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;test_tfidf&amp;nbsp;=&amp;nbsp;m_tfidf.transform([vtext]) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;proba&amp;nbsp;=&amp;nbsp;model.predict_proba(test_tfidf)[0] &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f'긍정&amp;nbsp;확률&amp;nbsp;:&amp;nbsp;{proba[1]}') &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;f_review('배송도&amp;nbsp;빠르고&amp;nbsp;실물이&amp;nbsp;너무&amp;nbsp;예뻐요&amp;nbsp;대만족입니다',&amp;nbsp;model) &lt;br /&gt;f_review('............') &lt;br /&gt;f_review('') &lt;br /&gt;&lt;br /&gt;import pandas as pd &lt;br /&gt;from&amp;nbsp;konlpy.tag&amp;nbsp;import&amp;nbsp;Okt &lt;br /&gt;&lt;br /&gt;okt&amp;nbsp;=&amp;nbsp;Okt() &lt;br /&gt;&lt;br /&gt;test&amp;nbsp;=&amp;nbsp;'배송도&amp;nbsp;빠르고&amp;nbsp;실물이&amp;nbsp;너무&amp;nbsp;예뻐요&amp;nbsp;대만족입니다' &lt;br /&gt;&lt;br /&gt;def&amp;nbsp;f_review(text,&amp;nbsp;model): &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;한글만&amp;nbsp;남기기 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;vtext&amp;nbsp;=&amp;nbsp;pd.Series([text]).replace( &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;r'[^가-힣\s]', &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;'', &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;regex=True &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;).iloc[0] &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;형태소&amp;nbsp;분석 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;tokens&amp;nbsp;=&amp;nbsp;okt.morphs(vtext,&amp;nbsp;stem=True) &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;불용어&amp;nbsp;제거 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;tokens&amp;nbsp;=&amp;nbsp;[word&amp;nbsp;for&amp;nbsp;word&amp;nbsp;in&amp;nbsp;tokens&amp;nbsp;if&amp;nbsp;word&amp;nbsp;not&amp;nbsp;in&amp;nbsp;stops] &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;TF-IDF&amp;nbsp;입력용&amp;nbsp;문자열로&amp;nbsp;변환 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;clean_text&amp;nbsp;=&amp;nbsp;'&amp;nbsp;'.join(tokens) &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;기존에&amp;nbsp;학습한&amp;nbsp;TF-IDF&amp;nbsp;객체로&amp;nbsp;변환 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;test_tfidf&amp;nbsp;=&amp;nbsp;m_tfidf.transform([clean_text]) &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;긍정&amp;nbsp;확률&amp;nbsp;계산 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;proba&amp;nbsp;=&amp;nbsp;model.predict_proba(test_tfidf)[0] &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print('전처리&amp;nbsp;결과:',&amp;nbsp;clean_text) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f'부정&amp;nbsp;확률:&amp;nbsp;{proba[0]:.4f}') &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f'긍정&amp;nbsp;확률:&amp;nbsp;{proba[1]:.4f}') &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;proba &lt;br /&gt;&lt;br /&gt;f_review('배송도&amp;nbsp;빠르고&amp;nbsp;실물이&amp;nbsp;너무&amp;nbsp;예뻐요&amp;nbsp;대만족입니다',&amp;nbsp;model)&lt;/p&gt;
&lt;pre id=&quot;code_1785303242347&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;f_review('배송도 빠르고 실물이 너무 예뻐요 대만족입니다', model)
전처리 결과: 배송 빠르다 실물 너무 예쁘다 대 만족 이다
부정 확률: 0.0884
긍정 확률: 0.9116
Out[128]: array([0.08844871, 0.91155129])&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;04 쇼핑몰&amp;nbsp;후기&amp;nbsp;감성&amp;nbsp;분석&amp;nbsp;-&amp;nbsp;모델링(RNN)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;1.&amp;nbsp;데이터&amp;nbsp;로딩 &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;2.&amp;nbsp;공통&amp;nbsp;전처리 &lt;br /&gt;#&amp;nbsp;1)&amp;nbsp;정제 &lt;br /&gt;#&amp;nbsp;2)&amp;nbsp;토큰화&amp;nbsp;+&amp;nbsp;어간추출 &lt;br /&gt;#&amp;nbsp;3)&amp;nbsp;불용어&amp;nbsp;제거 &lt;br /&gt;train_token &lt;br /&gt;test_token &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;3.&amp;nbsp;벡터화 &lt;br /&gt;from&amp;nbsp;tensorflow.keras.preprocessing.text&amp;nbsp;import&amp;nbsp;Tokenizer &lt;br /&gt;from&amp;nbsp;tensorflow.keras.preprocessing.sequence&amp;nbsp;import&amp;nbsp;pad_sequences &lt;br /&gt;&lt;br /&gt;model_token&amp;nbsp;=&amp;nbsp;Tokenizer() &lt;br /&gt;model_token.fit_on_texts(train_token) &lt;br /&gt;model_token.word_index&amp;nbsp;&amp;nbsp;#&amp;nbsp;정수&amp;nbsp;인코딩&amp;nbsp;결과&amp;nbsp;('마사지':999,&amp;nbsp;'게임':1000)&lt;/p&gt;
&lt;pre id=&quot;code_1785305571780&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;model_token.word_index  # 정수 인코딩 결과 ('마사지':999, '게임':1000)
Out[141]: 
{'하다': 1,
 '좋다': 2,
 '에': 3,
 '너무': 4,
 '배송': 5,
 '은': 6,
 '자다': 7,
 '있다': 8,
 '같다': 9,
 '안': 10,
 ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;정수인코딩&amp;nbsp;룰에&amp;nbsp;의한&amp;nbsp;텍스트&amp;nbsp;변환 &lt;br /&gt;train_num&amp;nbsp;=&amp;nbsp;model_token.texts_to_sequences(train_token) &lt;br /&gt;test_num&amp;nbsp;=&amp;nbsp;model_token.texts_to_sequences(test_token)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;4.&amp;nbsp;패딩 &lt;br /&gt;#&amp;nbsp;-&amp;nbsp;서로&amp;nbsp;길이가&amp;nbsp;다른&amp;nbsp;문장을&amp;nbsp;동일&amp;nbsp;길이로&amp;nbsp;맞추는&amp;nbsp;작업 &lt;br /&gt;#&amp;nbsp;-&amp;nbsp;padding&amp;nbsp;size&amp;nbsp;결정 &lt;br /&gt;wc&amp;nbsp;=&amp;nbsp;[len(text_list)&amp;nbsp;for&amp;nbsp;text_list&amp;nbsp;in&amp;nbsp;train_token] &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;**&amp;nbsp;히스토그램&amp;nbsp;시각화&amp;nbsp;(문장&amp;nbsp;길이&amp;nbsp;분포&amp;nbsp;확인&amp;nbsp;목적) &lt;br /&gt;import&amp;nbsp;matplotlib.pyplot&amp;nbsp;as&amp;nbsp;plt &lt;br /&gt;plt.hist(wc,&amp;nbsp;bins=30)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;565&quot; data-origin-height=&quot;425&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/X22um/dJMb99NX8Sw/maKFnbSWXEQztKCITBXNE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/X22um/dJMb99NX8Sw/maKFnbSWXEQztKCITBXNE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/X22um/dJMb99NX8Sw/maKFnbSWXEQztKCITBXNE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FX22um%2FdJMb99NX8Sw%2FmaKFnbSWXEQztKCITBXNE1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;565&quot; height=&quot;425&quot; data-origin-width=&quot;565&quot; data-origin-height=&quot;425&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 제로 패딩 : 짧은 문장을 긴 문장의 길이에 맞추는 과정에서 짧은 문장의 뒷쪽을 0으로 채워 길이를 맞추는 작업&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;**&amp;nbsp;상위&amp;nbsp;95%를&amp;nbsp;커버하는&amp;nbsp;길이로&amp;nbsp;설정 &lt;br /&gt;import&amp;nbsp;numpy&amp;nbsp;as&amp;nbsp;np &lt;br /&gt;maxlen&amp;nbsp;=&amp;nbsp;int(np.percentile(wc,&amp;nbsp;95))&lt;/p&gt;
&lt;pre id=&quot;code_1785307157491&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;maxlen
Out[153]: 33&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;2)&amp;nbsp;패딩&amp;nbsp;변환 &lt;br /&gt;train_pad&amp;nbsp;=&amp;nbsp;pad_sequences(train_num,&amp;nbsp;maxlen&amp;nbsp;=&amp;nbsp;maxlen) &lt;br /&gt;test_pad&amp;nbsp;=&amp;nbsp;pad_sequences(test_num,&amp;nbsp;maxlen&amp;nbsp;=&amp;nbsp;maxlen) &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;5.&amp;nbsp;모델링 &lt;br /&gt;from&amp;nbsp;tensorflow.keras.models&amp;nbsp;import&amp;nbsp;Sequential &lt;br /&gt;from&amp;nbsp;tensorflow.keras.layers&amp;nbsp;import&amp;nbsp;Dense,&amp;nbsp;SimpleRNN,&amp;nbsp;LSTM,&amp;nbsp;Embedding &lt;br /&gt;from&amp;nbsp;tensorflow.keras.callbacks&amp;nbsp;import&amp;nbsp;EarlyStopping &lt;br /&gt;import&amp;nbsp;tensorflow&amp;nbsp;as&amp;nbsp;tf &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;seed&amp;nbsp;고정 &lt;br /&gt;seed&amp;nbsp;=&amp;nbsp;0 &lt;br /&gt;np.random.seed(seed) &lt;br /&gt;tf.random.set_seed(seed) &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;임베딩&amp;nbsp;차원&amp;nbsp;(hyper&amp;nbsp;parameter) &lt;br /&gt;#&amp;nbsp;2,&amp;nbsp;4,&amp;nbsp;8,&amp;nbsp;16,&amp;nbsp;32,&amp;nbsp;64,&amp;nbsp;128&amp;nbsp;등&amp;nbsp;결정 &lt;br /&gt;embedding_dim&amp;nbsp;=&amp;nbsp;64 &lt;br /&gt;hidden_units&amp;nbsp;=&amp;nbsp;128 &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;target&amp;nbsp;변수&amp;nbsp;변환 &lt;br /&gt;train_y_dm&amp;nbsp;=&amp;nbsp;pd.get_dummies(train_y).astype(int).values &lt;br /&gt;test_y_dm&amp;nbsp;=&amp;nbsp;pd.get_dummies(test_y).astype(int).values &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;모델&amp;nbsp;생성 &lt;br /&gt;model_rnn&amp;nbsp;=&amp;nbsp;Sequential() &lt;br /&gt;model_rnn.add(Embedding(word_cnt,&amp;nbsp;embedding_dim)) &lt;br /&gt;model_rnn.add(SimpleRNN(hidden_units,&amp;nbsp;activation&amp;nbsp;=&amp;nbsp;'tanh')) &lt;br /&gt;model_rnn.add(Dense(2,&amp;nbsp;activation&amp;nbsp;=&amp;nbsp;'softmax')) &lt;br /&gt;&lt;br /&gt;model_rnn.compile('adam',&amp;nbsp;loss='binary_crossentropy',&amp;nbsp;metrics=['accuracy']) &lt;br /&gt;es&amp;nbsp;=&amp;nbsp;EarlyStopping(monitor='val_loss',&amp;nbsp;patience&amp;nbsp;=&amp;nbsp;10,&amp;nbsp;restore_best_weights&amp;nbsp;=&amp;nbsp;True) &lt;br /&gt;model_rnn.fit(train_pad,&amp;nbsp;train_y_dm,&amp;nbsp;epochs=100,&amp;nbsp;validation_split=0.25,&amp;nbsp;batch_size&amp;nbsp;=&amp;nbsp;32,&amp;nbsp;callbacks&amp;nbsp;=&amp;nbsp;[es]&amp;nbsp;) &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;모델&amp;nbsp;평가 &lt;br /&gt;model_rnn.evaluate(train_pad,&amp;nbsp;train_y_dm)[1]&amp;nbsp;#&amp;nbsp;train&amp;nbsp;accuracy &lt;br /&gt;model_rnn.evaluate(test_pad,&amp;nbsp;test_y_dm)[1]&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;test&amp;nbsp;accuracy&lt;/p&gt;
&lt;pre id=&quot;code_1785308856780&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;model_rnn.evaluate(train_pad, train_y_dm)[1] # train accuracy
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - accuracy: 0.9336 - loss: 0.1916 
Out[166]: 0.9336000084877014

model_rnn.evaluate(test_pad, test_y_dm)[1]   # test accuracy
79/79 ━━━━━━━━━━━━━━━━━━━━ 0s 3ms/step - accuracy: 0.8596 - loss: 0.3522 
Out[167]: 0.8596000075340271&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;model_rnn.predict(train_pad[[0]])[0][1] &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;6.&amp;nbsp;리뷰&amp;nbsp;테스트 &lt;br /&gt;def&amp;nbsp;f_review_rnn(text,&amp;nbsp;model)&amp;nbsp;: &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;vtext&amp;nbsp;=&amp;nbsp;Series(text).replace('[^가-힣&amp;nbsp;]',&amp;nbsp;'',&amp;nbsp;regex=True)[0]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;정제 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;vtext = Okt.morphs(vtext, stem=True)&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;# 토큰화 + 어간추출 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;vtext&amp;nbsp;=&amp;nbsp;[word&amp;nbsp;for&amp;nbsp;word&amp;nbsp;in&amp;nbsp;vtext&amp;nbsp;if&amp;nbsp;word&amp;nbsp;not&amp;nbsp;in&amp;nbsp;stops]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;불용어&amp;nbsp;제거 &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;train_pad&amp;nbsp;=&amp;nbsp;...... &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;proba&amp;nbsp;=&amp;nbsp;model.predict(train_pad) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f'긍정&amp;nbsp;확률&amp;nbsp;:&amp;nbsp;{proba[1]}') &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;f_review_rnn('배송도&amp;nbsp;빠르고&amp;nbsp;실물이&amp;nbsp;너무&amp;nbsp;예뻐요&amp;nbsp;대만족입니다',&amp;nbsp;model) &lt;br /&gt;f_review_rnn('초등학생들이나&amp;nbsp;좋아하겠네요',&amp;nbsp;model) &lt;br /&gt;f_review_rnn('강의가&amp;nbsp;깔끔하네요...&amp;nbsp;근데&amp;nbsp;시험은&amp;nbsp;떨어진것같아요ㅜㅜㅜㅜㅜ&amp;nbsp;',&amp;nbsp;model) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>아이티윌_데이터 분석 55기/강의내용_딥러닝</category>
      <author>ecosso</author>
      <guid isPermaLink="true">https://ecosso.tistory.com/148</guid>
      <comments>https://ecosso.tistory.com/148#entry148comment</comments>
      <pubDate>Wed, 29 Jul 2026 16:16:45 +0900</pubDate>
    </item>
    <item>
      <title>[골목길 탐지하기]_위성영상 탐색과 활용 : Sentinel-2</title>
      <link>https://ecosso.tistory.com/147</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;중간 프로젝트를 진행하며 도로망 데이터에 대한 아쉬움을 매우 크게 느끼고 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이륜차 사고다발지역에 대한 특성이다보니 일반적인 승용차와 다르게 협소한 골목, 공원 내, 아파트 혹은 대학교 부지 내 도로 등에서도 사고가 발생한 지점이 있었는데,&amp;nbsp; 분석에 활용한 도로망 데이터는 대부분 교통 네트워크 구축 혹은 행정 목적에 맞춰 제작된 데이터였기에 원하는 해상도에서의 생활도로까지는 충분히 표현되지 않은 경우가 존재했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;서울시 내 생활도로, 좁은 골목까지 모두 도로를 새로 작도하기에는 시간적인 여유가 없었기에 도로 데이터가 존재하지 않는 사고 포인트도 10개 미만이었기에 인접 격자에서의 최솟값들을 가져와 대치하는 것으로 마무리했었던 기억이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그러던 중 딥러닝 수업을 들으며 문득 새로운 가능성이 떠올랐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 도로망 데이터에 중첩되는 곳이 도로로 학습된다는 가정 하에, CNN을 통해 해당 패턴에 부합하는 장소들을 찾게 된다면 미세도로망까지도 모두 탐지할 수 있지 않을까?라는 의문이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;즉, 학습된 모델이 도로의 형태를 인식해 자동으로 벡터 형태의 도로망을 생성하는 프로젝트이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;해당 프로젝트를 위해서는 우선 고해상도 항공영상이 필요한 상황이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 무료로 10m 급의 해상도까지 제공해주는 항공영상을 찾게 되었고, 해당 영상이 어떤 위성에서 어떤 목적으로 어떻게 촬영되었는지를 기본으로 이 프로젝트를 진행해보려고 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;(vworld가 훨씬 더 해상도가 높으나, 우선은 실험적으로 sentinel-2 영상을 고려하고 있다.)&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;01 CDSE&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;우선 CDSE (Copernicus Data Space Ecosystem)에서 위성영상을 무료로 다운받을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이는 유럽연합의 Copernicus 프로그램을 위하여 개발 및 운영되고 있는 공식 위성 데이터 플랫폼이며, 이 프로그램을 수행하기 위해 만든 위성들이 Sentinel 시리즈이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;따라서 해당 페이지 내에서 Sentinel 영상들의 조회 및 다운로드가 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;또한 원하는 날짜, 구름량, 지역, 위성의 종류 등을 검색할 수 있으며 클라우드 내에서 NDVI 계산 등을 직접 수행할 수 있게 서비스가 제공되고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://browser.dataspace.copernicus.eu/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://browser.dataspace.copernicus.eu/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1785138916113&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Copernicus Browser&quot; data-og-description=&quot;Search, visualise and download Sentinel satellite imagery&quot; data-og-host=&quot;browser.dataspace.copernicus.eu&quot; data-og-source-url=&quot;https://browser.dataspace.copernicus.eu/&quot; data-og-url=&quot;https://browser.dataspace.copernicus.eu/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/db9JbE/dJMb83SwQmC/vO09P1CMUCG0JvP5sN2TRk/img.jpg?width=300&amp;amp;height=300&amp;amp;face=0_0_300_300,https://scrap.kakaocdn.net/dn/MWp9r/dJMb84YcxgV/I0ji6FCL58zTePjr97Ib10/img.jpg?width=300&amp;amp;height=300&amp;amp;face=0_0_300_300&quot;&gt;&lt;a href=&quot;https://browser.dataspace.copernicus.eu/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://browser.dataspace.copernicus.eu/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/db9JbE/dJMb83SwQmC/vO09P1CMUCG0JvP5sN2TRk/img.jpg?width=300&amp;amp;height=300&amp;amp;face=0_0_300_300,https://scrap.kakaocdn.net/dn/MWp9r/dJMb84YcxgV/I0ji6FCL58zTePjr97Ib10/img.jpg?width=300&amp;amp;height=300&amp;amp;face=0_0_300_300');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Copernicus Browser&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Search, visualise and download Sentinel satellite imagery&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;browser.dataspace.copernicus.eu&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 Data &amp;amp; Information에 들어가 어떤 위성영상들을 다운로드 받을 수 있는지 살펴보았다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1038&quot; data-origin-height=&quot;680&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nqdjM/dJMcaaTG06B/hbv1W4zjqQnimalKwAVE1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nqdjM/dJMcaaTG06B/hbv1W4zjqQnimalKwAVE1k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nqdjM/dJMcaaTG06B/hbv1W4zjqQnimalKwAVE1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnqdjM%2FdJMcaaTG06B%2Fhbv1W4zjqQnimalKwAVE1k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1038&quot; height=&quot;680&quot; data-origin-width=&quot;1038&quot; data-origin-height=&quot;680&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Sentinel-1&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Sentinel-1은 C-band SAR(Synthetic Aperture Radar) 센서를 탑재한 레이더 위성으로, 위성에서 직접 전자파를 발사하고 지표면에서 반사되어 돌아오는 신호를 수신하여 영상을 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;광학위성과 달리 태양광을 이용하지 않기 때문에 야간에도 촬영이 가능하며 구름, 비의 영향이 거의 없다. 따라서 육상 및 해양 모니터링을 위해서 주야간의 구분 없이 모든 기상 조건에서 지속적으로 관측이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;주로 홍수 및 침수지역 탐지, 산림 변화 감지, 농경지 및 토양수분, 도시 변화, 지반침하 등을 확인하는데에 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;다만 표면에서 반사되어 오는 반사파를 측정하는 것이기에 표면의 거칠기, 구조물, 지형, 수분 함량 등을 측정할 수 있으나 콘크리트, 물과 같은 재료의 특성에서 오는 반사파가 다르다는 특성을 반영한 것이지, 색상에 의한 구분은 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(레이더가 얼마나 강하게 반사되었는가를 색으로 표현하였을 뿐이다. 따라서 우리가 실제로 눈으로 보는 것과는 다른 색으로 표현된다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;원래는 Sentinel-1A, Sentinel-1B 두 대의 위성이 운영되고 있었으나 1B의 이상으로 인해 2022년 임무가 종료된 상황이며, 2년 후인 2024년에 1C가 추가적으로 발사되어 현재 성능검증 중에 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;일반적으로 10m / 30m / 90m 세 가지의 해상도를 제공하고 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Sentinel-2&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Sentinel-2는 육상 관측을 위한 두 대의 위성으로 구성된 관측 시스템이다. 두 대의 위성이 &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;180&amp;deg; 떨어진 위치에서 같은 궤도를 따라 운행하며 영상을 생성하며, 약 5일에 한 번씩 같은 장소를 촬영한다. 중위도의 경우에는 2-3일에 한 번씩 촬영하기 때문에 한국의 경우 해당 주기로 촬영이 가능하다. 현재 기존의 Sentinel-2A를 대신할 Sentinel-2C이 2024년 발사되어 현재는 총 3대의 위성이 동시에 운영중이다. (2C가 2A의 역할을 대신 수행중)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&amp;nbsp;또한 Sentinel-2는 MSI (&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;Multi-Spectral Instrument)라는 다중분광 카메라가 장착되어 있으며, 해당 카메라는 13개의 밴드를 촬영한다. (10m 밴드: 4개, 20m 밴드 : 6개, 60m 밴드 : 3개) 또한 지구를 촬영 시 각 타일은 110 X 110 ㎞ 크기의 정사각형 타일로 나누어 저장한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&amp;nbsp;Sentinel-2의 Level 1C, 1B의 경우 센서가 측정한 원시 데이터와 대기 상단 반사율(TOA) 데이터로써 주로 위성영상의 전처리, 보정 등에 사용된다. 아직 대기의 영향을 제거하지 않은 영상으로 일반적인 토지피복 분류에 사용되는 영상이라기보다는 영상 전처리 및 알고리즘 개발에 주로 활용된다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&amp;nbsp;Sentinel-2의 Level 2A의 경우 Level 1C에서부터 생성된 대기보정 지표반사율 영상을 제거한다. 이는 구름, 수증기, 대기의 반사 및 산란 등에 대한 보정을 진행한 영상이다. 따라서 에어로졸 광학두께(AOT : Aerosol Optical Thickness), 수증기 (WV : Water Vapour),&amp;nbsp; 장면분류 (SCL : Scence Classification) 등 역시 추가적인 영상도 Level 2A에 포함된다. 이러한 영상 산출물 및 분광 밴드별 지표반사율 자료는 각각 10m, 20, 60m의 해상도로 제공된다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&amp;nbsp;- &lt;/span&gt;&lt;/span&gt;Sentinel-2 Level 2A WorldCover Annual Cloudless Mosaic (RGBNIR)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&amp;nbsp;연간 무운 모자이크로, 해당 합성영상은 전 지구 규모의 구름 없는 10m 해상도 분석용 모자이크이다. 2020~2021년의 연간 Sentinel-2 아카이브로부터 생성되었으며, 각 밴드의 연간 시계열 자료에서 구름 픽셀을 제거한 뒤, 남은 값들의 중앙값을 계산하여 제작되었다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&amp;nbsp;RGBNIR 모자이크는 다음과 같은 10m 밴드를 포함한다. : B04(Red), B03(Green), B02(Blue), B08(Near Infrared)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&amp;nbsp;COG (Cloud Optimized GeoTIFF) 형식으로 제공되며, Copernicus Broweser에서 확인 가능하다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;3. Sentinel-3&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #212529;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&amp;nbsp;Sentinel-3는 해양 및 육상 표면의 색상, 해수면 및 지표면 온도, 해수면 지형을 높은 정확도로 측정하여 해양 예측, 환경 모니터링, 기후 변화 모니터링 등을 수행하기 위한 영상을 지원한다. 기존 ERS, ENVISAT, SPOT Vegetaion 위성들이 관측하던 자료를 이어받으며 센서 성능 및 관측 범위를 더욱 향상시키기 위해 개발되었다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #212529;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&amp;nbsp;&amp;gt; &lt;b&gt;ERS(European Remote Sensing Satellite)&lt;/b&gt;: &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #212529;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;ESA가 운영한 유럽 최초의 본격적인 지구관측 위성 시리즈로, 레이더를 이용해 해양&amp;middot;빙하&amp;middot;육지와 지표 변화를 관측&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #212529;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&amp;nbsp;&amp;gt; &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ENVISAT(Environmental Satellite)&lt;/b&gt;:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;ERS의 후속 대형 환경관측 위성으로, 레이더와 광학&amp;middot;대기 센서를 함께 탑재해 해양, 육상, 대기, 기후를 종합적으로 관측&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&amp;gt; &lt;b&gt;SPOT Vegetation&lt;/b&gt;:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; 프랑스 SPOT 위성에 탑재된 광역 식생 관측 센서로, 전 세계 식생 분포와 생육 상태, 토지피복 변화를 지속적으로 모니터링&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;Sentinel-3의 경우 OLCI Level-1, &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;OLCI&lt;/span&gt; Level-2가 있는데, Level-1은 OLCI(Ocean and Land Colour Instrument) 센서에서 취득한 자료를 방사보정 (센서 오차를 보정), 위치보정 (영상을 실제 위치에 맞추는 보정), 정사보정 (지형 왜곡을 보정)하며, Level-2는 Level-1 자료를 이용하여 계산한 지구물리학적 변수를 제공한다. Level -2는 육상 환경 변수, 대기 환경 변수, Full Resolution 및 Reduced Resolution 자료, 데이터 품질 플래그, 대기보정 정보 등을 포함한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;또한 SLSTR Level-1, Level-2의 경우 Level-1은 주요 물리량, Level-2는 Level-1보다 높은 수준의 지구물리학적 정보를 제공한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&amp;nbsp;SYN Level-2의 경우 OLCL와 SLSTR 센서의 Level-1 및 Level-2를 결합하여 생성되었으며 대기 및 대기 구성성문에 대한 정보를 제공한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;SRAL Level-1, Level-2의 경우 마찬가지로 SRAL의 원시데이터를 보정 및 검증한 기본 처리 자료, 해당 기본 처리 자료를 기반으로 추가 처리하여 생성한 지구물리학 제품이다. 해수면 높이, 유의파고, 풍속 등에 대한 정보를 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;POD는 위성의 궤도를 매우 높은 정확도로 계산한 보조 데이터로, NRT, STC, NTC 등에 대한 정보를 제공한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. Sentinel-5P&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sentinel-5P는 대기 관측을 전담하는 위성이다. 주요 목적은 높은 시공간적 해상도로 대기를 관측하여 대기질, 오존 및 자외선 관측, 기후 모니터링 및 예측 등에 대한 정보를 지원하는 것이다. TROPOMI(TROPOspheric Monitoring Instrument) 센서를 탑재하고 있으며 처리 수준에 따라 Level-0, 1B, 2 제품을 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Sentinel-5P Level 2의 경우 여러가지 항목에 대한 정보를 제공한다. :&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 에어로졸 지수 또는 흡수성 에어로졸 지수 : 이는 황사, 산불 연기, 화산재 등에 활용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 일산화탄소 : 대기오염, 산불 영향, 배출원 분석 등에 활용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 구름 : 대기 보정, 기후 연구, 복사전달 연구 등에 활용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 포름알데히드 : 광화학 스모그, VOC (휘발성유기화합물) 연구, 대기화학 연구 등에 활용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 메탄 : 기후변화 연구, 메탄 배출원 분석, 온실가스 감시, 환경정책 수립 등에 활용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 이산화질소 : 대기오염, 도시환경 분석, 산업활동 모니터링 등에 활용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 오존 : 오존층 감소 모니터링, 극지방 오존홀 연구, 대기질 평가, 오존 오염 관리 등에 활용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 이산화황 : 화산활동 감시, 대기오염, 대기화학 연구, 인체 건강 영향 평가 등에 활용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. Sentinel-6&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Sentinel-6은 평균 해수면 및 해양 상태와 같은 해양 환경 관측 목적으로 운용되는 두 개의 위성이다. Poseidon-4 SAR 고도계, AMR-C (기후연구용 다중주파수 마이크로파 복사계), POD(정밀 궤도 결정 시스템) 등의 센서가 탑재되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sentinel-6의 경우 크게 3개의 주요 데이터 제품을 제공한다. :&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;- Level-1 : Poseidon-4 고도계에서 관측한 원시 신호를 보정하여 거리, 파형 등의 기본 관측값을 제공&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;- Level-2 : Level-1 자료를 추가 처리하여 해수면 높이, 유의파고, 해상풍속 등 실제 해양 분석에 활용할 수 있는 지구물리학적 변수를 제공&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;- 보조자료(Auxiliary Data) : 위성의 정밀 궤도, 기상 및 대기 보정 등 Level-1과 Level-2 자료를 정확하게 처리하기 위해 필요한 보조 정보를 제공&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Sentinel 시리즈를 살펴본 결과, 이번 도로 탐지 실험에는 Sentinel-2가 가장 적합하다고 판단하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Sentinel-1은 기상과 주야간에 관계없이 관측할 수 있다는 장점이 있지만 SAR 영상이므로 일반적인 항공사진과 영상 특성이 크게 다르다. Sentinel-3와 Sentinel-6은 해양 및 광역 환경관측, Sentinel-5P는 대기환경 관측을 주요 목적으로 하기 때문에 도로와 같은 육상의 세부 객체를 영상에서 구분하려는 이번 목적과는 차이가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;반면 Sentinel-2는 육상 관측을 목적으로 하는 광학위성이며, 가시광선 RGB와 근적외선(NIR)을 포함한 다양한 분광 정보를 제공한다. 특히 B02(Blue), B03(Green), B04(Red), B08(NIR) 밴드는 10m 공간해상도로 제공되므로 Sentinel 시리즈 가운데 육상의 도로, 건물, 식생 등 지표면의 공간적 패턴을 구분하는 데 비교적 적합하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이번 프로젝트에서 가장 중요하게 생각한 점은 기존 도로망에 포함된 도로의 영상 패턴을 학습한 모델이 기존 데이터에 존재하지 않는 도로까지 찾아낼 수 있는가이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;따라서 우선 기존 도로망을 Sentinel-2 영상과 중첩하여 도로에 해당하는 영역을 학습 라벨로 구축하고, CNN 기반 이미지 분할 모델을 통해 영상에서 도로와 유사한 공간적&amp;middot;분광적 특징을 갖는 영역을 탐지해보고자 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;다만 Sentinel-2의 최고 공간해상도는 10m이므로 이번 프로젝트의 궁극적인 목표인 협소한 생활도로와 골목길을 정밀하게 구분하기에는 한계가 있을 것으로 예상된다. 폭이 10m보다 좁은 도로는 하나의 픽셀 안에 건물, 식생, 도로 등이 함께 포함될 수 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;따라서 Sentinel-2는 최종 도로망 구축을 위한 데이터라기보다, 공개 위성영상과 기존 도로 데이터를 이용해 딥러닝 기반 도로 탐지가 가능한지 확인하기 위한 초기 실험 데이터로 활용하고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이후 실험에서 공간해상도의 한계가 확인될 경우, 보다 고해상도의 정사영상이나 항공영상을 이용하여 동일한 방법을 적용하고 결과를 비교할 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;+) 이후 학습을 위한 전국 표준 노드링크 데이터는 위성사진 촬영 직전의 가장 최근 날짜인 2026년 06월 12일 데이터를 사용하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.its.go.kr/nodelink/nodelinkRef&quot;&gt;전국표준노드링크 | ITS 국가교통정보센터(National Transport Information Center)&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1785139256435&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;ITS 국가교통정보센터&quot; data-og-description=&quot;ITS 국가교통정보센터&quot; data-og-host=&quot;its.go.kr&quot; data-og-source-url=&quot;https://www.its.go.kr/nodelink/nodelinkRef&quot; data-og-url=&quot;https://its.go.kr&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dPOt0S/dJMb88GiTkL/TFlUPICryDheA8S7n8nqhK/img.png?width=390&amp;amp;height=350&amp;amp;face=0_0_390_350&quot;&gt;&lt;a href=&quot;https://www.its.go.kr/nodelink/nodelinkRef&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.its.go.kr/nodelink/nodelinkRef&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dPOt0S/dJMb88GiTkL/TFlUPICryDheA8S7n8nqhK/img.png?width=390&amp;amp;height=350&amp;amp;face=0_0_390_350');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;ITS 국가교통정보센터&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;ITS 국가교통정보센터&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;its.go.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;798&quot; data-origin-height=&quot;127&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7J1at/dJMcaiqDBrH/sw4lRGEtxQGBbxZRxmsCLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7J1at/dJMcaiqDBrH/sw4lRGEtxQGBbxZRxmsCLK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7J1at/dJMcaiqDBrH/sw4lRGEtxQGBbxZRxmsCLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7J1at%2FdJMcaiqDBrH%2Fsw4lRGEtxQGBbxZRxmsCLK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;798&quot; height=&quot;127&quot; data-origin-width=&quot;798&quot; data-origin-height=&quot;127&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;+) 실험 결과 :&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;실제로 Sentinel-2 영상과 표준노드링크를 이용하여 도로 탐지를 시도한 결과, 예상했던 것처럼 학습 데이터와 영상 해상도 양쪽에서 한계가 나타났다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;우선 정답 데이터로 사용한 표준노드링크에는 주요 도로는 비교적 잘 구축되어 있었지만, 이번 프로젝트에서 탐지하고자 했던 좁은 골목길과 생활도로가 모두 포함되어 있지는 않았다. 기존 도로망과 중첩되는 영상의 특징을 도로로 학습시키는 방식이므로, 애초에 정답 데이터에 포함되지 않은 골목길의 시각적 특징은 모델이 충분히 학습하기 어려웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;여기에 Sentinel-2의 10m 공간해상도도 문제가 되었다. 실제 영상을 확대해보면 광폭도로는 어느 정도 형태를 확인할 수 있었지만, 좁은 골목길은 하나의 픽셀보다 폭이 작거나 주변의 건물&amp;middot;식생&amp;middot;그림자 등과 함께 하나의 픽셀에 포함되는 경우가 많았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;따라서 도로와 비도로의 경계가 육안으로도 명확하게 구분되지 않았으며, 모델 역시 이러한 협소도로를 안정적으로 탐지하지 못했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;결과적으로 초기 실험에서는 주요 도로는 어느 정도 탐지할 수 있었지만, 본래 목표로 했던 골목길과 생활도로를 새롭게 찾아내는 데에는 뚜렷한 한계가 있었다. 이는 단순히 모델의 성능 문제라기보다, 학습에 사용한 기존 도로망에 미세도로가 충분히 포함되지 않았다는 점과 입력 영상의 공간해상도가 목표 객체의 크기에 비해 낮다는 점이 함께 작용한 결과로 판단하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;따라서 모델 구조나 학습 조건을 계속 조정하기보다는 입력 데이터와 정답 데이터 자체를 개선하는 것이 우선이라고 판단하였다. 이후에는 국토정보플랫폼에서 제공하는 항공정사영상 등 보다 고해상도의 영상을 확보하여 실제 골목길의 형태와 경계를 식별할 수 있도록 하고, 미세도로를 보다 정확하게 반영할 수 있는 정답 데이터를 함께 구축하여 도로 탐지 실험을 다시 진행하고자 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;결과적으로 Sentinel-2를 이용한 이번 실험은 원하는 수준의 골목길 탐지에는 도달하지 못했지만, 딥러닝 기반 미세도로 탐지에서는 모델의 선택만큼이나 입력 영상의 공간해상도와 정답 라벨의 품질이 중요하다는 점을 확인하는 계기가 되었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1407&quot; data-origin-height=&quot;905&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JItjk/dJMcaiRThtd/GumKzsdyNmx6DcpKK4hmZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JItjk/dJMcaiRThtd/GumKzsdyNmx6DcpKK4hmZ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JItjk/dJMcaiRThtd/GumKzsdyNmx6DcpKK4hmZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJItjk%2FdJMcaiRThtd%2FGumKzsdyNmx6DcpKK4hmZ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1407&quot; height=&quot;905&quot; data-origin-width=&quot;1407&quot; data-origin-height=&quot;905&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>개인 프로젝트/골목길 탐지하기</category>
      <author>ecosso</author>
      <guid isPermaLink="true">https://ecosso.tistory.com/147</guid>
      <comments>https://ecosso.tistory.com/147#entry147comment</comments>
      <pubDate>Tue, 28 Jul 2026 18:24:08 +0900</pubDate>
    </item>
    <item>
      <title>#4 4일차_CNN, 얼굴 사진 분류하기, 쇼핑몰 후기 감성 분석</title>
      <link>https://ecosso.tistory.com/146</link>
      <description>&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;01 CNN (컨볼루션 신경망)&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;02 유명인사의 얼굴 데이터 분류하기&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-01 이미지 분석 - pca + knn (얼굴 데이터)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-02 이미지 분석 - ANN(3차원 학습 불가) (얼굴 데이터)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-03 이미지 분석 - CNN(3차원 학습 가능) (얼굴 데이터)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-04 새로운 얼굴 사진 예측 함수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-05&amp;nbsp;사전학습&amp;nbsp;모델(EfficientNetB0)&amp;nbsp;+&amp;nbsp;얼굴&amp;nbsp;데이터&amp;nbsp;전이학습&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;03 쇼핑몰 후기 감성 분석&lt;/b&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;01 CNN (컨볼루션 신경망)&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 심층신경망에 컨볼루션 망을 추가한 신경망&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp;-&lt;span&gt; &lt;/span&gt;&lt;/span&gt;컨볼루션 망에서 이미지의 정교한 특징을 추출&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp;-&lt;span&gt; &lt;/span&gt;&lt;/span&gt;마스크(=필터=윈도우)에 의해서 재추출된 신호&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp;-&lt;span&gt; &lt;/span&gt;&lt;/span&gt;hyper parameter : 컨볼루션층 수, 마스크 수, 마스크 사이즈&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp;-&lt;span&gt; &lt;/span&gt;&lt;/span&gt;parameter : 각 마스크 가중치&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 풀링(=서브샘플링) : 기존 이미지 신호 중 일부를 추출하여 보다 단순한 맵으로 결정 -&amp;gt; 과적합 해소&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;pool_size는 (2,2), (3,3), (4,4) 등을 사용 &amp;lt;- hyper marameter&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;pool에 의해 추출되는 신호는 주로 최댓값(max pooling), 평균값(average pooling) 등&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- Dropout : 의도적으로 특정 층의 중요하지 않은 노드(뉴런=Unit)를 꺼버리는 기법 -&amp;gt; 과적합 해소&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;size는 보통 0.5, 0.25 등을 주로 사용 &amp;lt;- hyper parameter&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;02 유명인사의 얼굴 데이터 분류하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-01 이미지 분석 - pca + knn (얼굴 데이터)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) 데이터 로딩 &lt;br /&gt;from&amp;nbsp;sklearn.datasets&amp;nbsp;import&amp;nbsp;fetch_lfw_people &lt;br /&gt;import&amp;nbsp;numpy&amp;nbsp;as&amp;nbsp;np&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;people&amp;nbsp;=&amp;nbsp;fetch_lfw_people(min_faces_per_person=50,&amp;nbsp;resize=0.7)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;스케일링&amp;nbsp;된&amp;nbsp;데이터 &lt;br /&gt;X&amp;nbsp;=&amp;nbsp;people['data'] &lt;br /&gt;X_images&amp;nbsp;=&amp;nbsp;people['images'] &lt;br /&gt;y&amp;nbsp;=&amp;nbsp;people['target'] &lt;br /&gt;yname&amp;nbsp;=&amp;nbsp;people['target_names'] &lt;br /&gt;n_classes&amp;nbsp;=&amp;nbsp;len(yname) &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;데이터&amp;nbsp;확인 &lt;br /&gt;import&amp;nbsp;matplotlib.pyplot&amp;nbsp;as&amp;nbsp;plt &lt;br /&gt;X_images[0].shape&lt;/p&gt;
&lt;pre id=&quot;code_1785208759016&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;X_images[0].shape
Out[159]: (87, 65)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;plt.imshow(X_images[1],&amp;nbsp;cmap='gray') &lt;br /&gt;yname[y[1]] &lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;2) 데이터 분리 &lt;br /&gt;from&amp;nbsp;sklearn.model_selection&amp;nbsp;import&amp;nbsp;train_test_split &lt;br /&gt;train_x,&amp;nbsp;test_x,&amp;nbsp;train_y,&amp;nbsp;test_y&amp;nbsp;=&amp;nbsp;train_test_split(X,&amp;nbsp;y,&amp;nbsp;random_state=0,&amp;nbsp;stratify=y) &lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style1&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;3) 모델링 &lt;br /&gt;#&amp;nbsp;1)&amp;nbsp;knn &lt;br /&gt;from&amp;nbsp;sklearn.neighbors&amp;nbsp;import&amp;nbsp;KNeighborsClassifier &lt;br /&gt;m_knn&amp;nbsp;=&amp;nbsp;KNeighborsClassifier() &lt;br /&gt;m_knn.fit(train_x,&amp;nbsp;train_y) &lt;br /&gt;m_knn.score(train_x,&amp;nbsp;train_y)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;0.6863 &lt;br /&gt;m_knn.score(test_x,&amp;nbsp;test_y)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;0.4974 &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;2)&amp;nbsp;pca&amp;nbsp;+&amp;nbsp;knn &lt;br /&gt;from&amp;nbsp;sklearn.decomposition&amp;nbsp;import&amp;nbsp;PCA &lt;br /&gt;from&amp;nbsp;sklearn.pipeline&amp;nbsp;import&amp;nbsp;make_pipeline &lt;br /&gt;&lt;br /&gt;pipe&amp;nbsp;=&amp;nbsp;make_pipeline(PCA(100),&amp;nbsp;KNeighborsClassifier(5)) &lt;br /&gt;pipe.fit(train_x,&amp;nbsp;train_y) &lt;br /&gt;pipe.score(train_x,&amp;nbsp;train_y)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;0.6965 &lt;br /&gt;pipe.score(test_x,&amp;nbsp;test_y)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;0.4974&amp;nbsp; &lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-02 이미지 분석 - ANN(3차원 학습 불가) (얼굴 데이터) &lt;br /&gt;1) 데이터 로딩 &lt;br /&gt;from&amp;nbsp;sklearn.datasets&amp;nbsp;import&amp;nbsp;fetch_lfw_people &lt;br /&gt;import&amp;nbsp;numpy&amp;nbsp;as&amp;nbsp;np&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;people&amp;nbsp;=&amp;nbsp;fetch_lfw_people(min_faces_per_person=50,&amp;nbsp;resize=0.7) &lt;br /&gt;X&amp;nbsp;=&amp;nbsp;people['data'] &lt;br /&gt;X_images&amp;nbsp;=&amp;nbsp;people['images'] &lt;br /&gt;y&amp;nbsp;=&amp;nbsp;people['target'] &lt;br /&gt;yname&amp;nbsp;=&amp;nbsp;people['target_names'] &lt;br /&gt;n_classes&amp;nbsp;=&amp;nbsp;len(yname) &lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style1&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;2) 데이터 분리 &lt;br /&gt;from&amp;nbsp;sklearn.model_selection&amp;nbsp;import&amp;nbsp;train_test_split &lt;br /&gt;train_x,&amp;nbsp;test_x,&amp;nbsp;train_y,&amp;nbsp;test_y&amp;nbsp;=&amp;nbsp;train_test_split(X,&amp;nbsp;y,&amp;nbsp;random_state=0,&amp;nbsp;stratify=y) &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;원핫&amp;nbsp;인코딩 &lt;br /&gt;import&amp;nbsp;pandas&amp;nbsp;as&amp;nbsp;pd &lt;br /&gt;train_y10&amp;nbsp;=&amp;nbsp;pd.get_dummies(train_y,&amp;nbsp;prefix='Y').astype(int).values &lt;br /&gt;test_y10&amp;nbsp;&amp;nbsp;=&amp;nbsp;pd.get_dummies(test_y,&amp;nbsp;prefix='Y').astype(int).values &lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style1&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;3) 모델링 &lt;br /&gt;#&amp;nbsp;1)&amp;nbsp;seed&amp;nbsp;고정 &lt;br /&gt;import&amp;nbsp;tensorflow&amp;nbsp;as&amp;nbsp;tf &lt;br /&gt;seed&amp;nbsp;=&amp;nbsp;0 &lt;br /&gt;np.random.seed(seed) &lt;br /&gt;tf.random.set_seed(seed) &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;2)&amp;nbsp;모델&amp;nbsp;정의 &lt;br /&gt;from&amp;nbsp;tensorflow.keras.layers&amp;nbsp;import&amp;nbsp;Input,&amp;nbsp;Dense &lt;br /&gt;from&amp;nbsp;keras&amp;nbsp;import&amp;nbsp;Sequential &lt;br /&gt;&lt;br /&gt;model&amp;nbsp;=&amp;nbsp;Sequential() &lt;br /&gt;model.add(Input(shape=(train_x.shape[1],&amp;nbsp;))) &lt;br /&gt;model.add(Dense(392,&amp;nbsp;activation='relu')) &lt;br /&gt;model.add(Dense(196,&amp;nbsp;activation='relu')) &lt;br /&gt;model.add(Dense(98,&amp;nbsp;activation='relu')) &lt;br /&gt;model.add(Dense(n_classes,&amp;nbsp;activation='softmax')) &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;3)&amp;nbsp;오차함수,&amp;nbsp;최적화&amp;nbsp;결정 &lt;br /&gt;model.compile(optimizer='adam',&amp;nbsp;loss='categorical_crossentropy',&amp;nbsp;metrics=['accuracy']) &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;4)&amp;nbsp;early&amp;nbsp;stopping&amp;nbsp;rule &lt;br /&gt;from&amp;nbsp;tensorflow.keras.callbacks&amp;nbsp;import&amp;nbsp;EarlyStopping &lt;br /&gt;es&amp;nbsp;=&amp;nbsp;EarlyStopping(monitor='val_loss',&amp;nbsp;patience=10,&amp;nbsp;restore_best_weights=True) &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;5)&amp;nbsp;학습 &lt;br /&gt;hist&amp;nbsp;=&amp;nbsp;model.fit(train_x,&amp;nbsp;train_y10,&amp;nbsp;validation_split=0.25, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;batch_size=10,&amp;nbsp;epochs=50000,&amp;nbsp;callbacks=[es]) &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;6)&amp;nbsp;평가 &lt;br /&gt;model.evaluate(train_x,&amp;nbsp;train_y10)[1]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;0.6871 &lt;br /&gt;model.evaluate(test_x,&amp;nbsp;test_y10)[1]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;0.5948 &lt;br /&gt;&lt;br /&gt;acc&amp;nbsp;=&amp;nbsp;hist.history['accuracy'] &lt;br /&gt;plt.plot(range(1,&amp;nbsp;len(acc)+1),&amp;nbsp;hist.history['accuracy'],&amp;nbsp;label='train') &lt;br /&gt;plt.plot(range(1,&amp;nbsp;len(acc)+1),&amp;nbsp;hist.history['val_accuracy'],&amp;nbsp;label='val') &lt;br /&gt;plt.legend() &lt;br /&gt;plt.xticks(range(1,&amp;nbsp;len(acc)+1)) &lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;561&quot; data-origin-height=&quot;422&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dJ6iKF/dJMcabLWPdz/Iu11PKCjxfaMr4uadNuAik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dJ6iKF/dJMcabLWPdz/Iu11PKCjxfaMr4uadNuAik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dJ6iKF/dJMcabLWPdz/Iu11PKCjxfaMr4uadNuAik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdJ6iKF%2FdJMcabLWPdz%2FIu11PKCjxfaMr4uadNuAik%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;561&quot; height=&quot;422&quot; data-origin-width=&quot;561&quot; data-origin-height=&quot;422&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-03 이미지 분석 - CNN(3차원 학습 가능) (얼굴 데이터) &lt;br /&gt;1) 데이터 로딩 &lt;br /&gt;from&amp;nbsp;sklearn.datasets&amp;nbsp;import&amp;nbsp;fetch_lfw_people &lt;br /&gt;import&amp;nbsp;numpy&amp;nbsp;as&amp;nbsp;np&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;people&amp;nbsp;=&amp;nbsp;fetch_lfw_people(min_faces_per_person=50,&amp;nbsp;resize=0.7) &lt;br /&gt;X&amp;nbsp;=&amp;nbsp;people['data'] &lt;br /&gt;X_images&amp;nbsp;=&amp;nbsp;people['images'] &lt;br /&gt;y&amp;nbsp;=&amp;nbsp;people['target'] &lt;br /&gt;yname&amp;nbsp;=&amp;nbsp;people['target_names'] &lt;br /&gt;n_classes&amp;nbsp;=&amp;nbsp;len(yname) &lt;br /&gt;h,&amp;nbsp;w&amp;nbsp;=&amp;nbsp;X_images.shape[1],&amp;nbsp;X_images.shape[2] &lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style1&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;2) 데이터 분리 및 변환 &lt;br /&gt;from&amp;nbsp;sklearn.model_selection&amp;nbsp;import&amp;nbsp;train_test_split &lt;br /&gt;train_x,&amp;nbsp;test_x,&amp;nbsp;train_y,&amp;nbsp;test_y&amp;nbsp;=&amp;nbsp;train_test_split(X_images,&amp;nbsp;y,&amp;nbsp;random_state=0,&amp;nbsp;stratify=y) &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;**&amp;nbsp;Conv2D&amp;nbsp;층은&amp;nbsp;(높이,&amp;nbsp;너비,&amp;nbsp;채널)&amp;nbsp;형태&amp;nbsp;요구&amp;nbsp;(흑백&amp;nbsp;-&amp;gt;&amp;nbsp;채널:1) &lt;br /&gt;train_x&amp;nbsp;=&amp;nbsp;train_x.reshape((train_x.shape[0],&amp;nbsp;h,&amp;nbsp;w,&amp;nbsp;1)) &lt;br /&gt;test_x&amp;nbsp;&amp;nbsp;=&amp;nbsp;test_x.reshape((test_x.shape[0],&amp;nbsp;h,&amp;nbsp;w,&amp;nbsp;1)) &lt;br /&gt;&lt;br /&gt;import&amp;nbsp;pandas&amp;nbsp;as&amp;nbsp;pd &lt;br /&gt;train_y10&amp;nbsp;=&amp;nbsp;pd.get_dummies(train_y).astype(int).values &lt;br /&gt;test_y10&amp;nbsp;&amp;nbsp;=&amp;nbsp;pd.get_dummies(test_y).astype(int).values &lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style1&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;3) 모델링 &lt;br /&gt;#&amp;nbsp;1)&amp;nbsp;seed&amp;nbsp;고정 &lt;br /&gt;import&amp;nbsp;tensorflow&amp;nbsp;as&amp;nbsp;tf &lt;br /&gt;seed&amp;nbsp;=&amp;nbsp;0 &lt;br /&gt;np.random.seed(seed) &lt;br /&gt;tf.random.set_seed(seed) &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;2)&amp;nbsp;모델&amp;nbsp;정의 &lt;br /&gt;from&amp;nbsp;tensorflow.keras.layers&amp;nbsp;import&amp;nbsp;Input,&amp;nbsp;Dense,&amp;nbsp;Conv2D,&amp;nbsp;Dropout,&amp;nbsp;Flatten,&amp;nbsp;MaxPooling2D &lt;br /&gt;from&amp;nbsp;keras&amp;nbsp;import&amp;nbsp;Sequential &lt;br /&gt;&lt;br /&gt;model&amp;nbsp;=&amp;nbsp;Sequential() &lt;br /&gt;model.add(Input(shape=(h,&amp;nbsp;w,&amp;nbsp;1))) &lt;br /&gt;model.add(Conv2D(32,&amp;nbsp;kernel_size=(2,&amp;nbsp;2),&amp;nbsp;activation='relu')) &lt;br /&gt;model.add(Conv2D(32,&amp;nbsp;kernel_size=(2,&amp;nbsp;2),&amp;nbsp;activation='relu')) &lt;br /&gt;model.add(MaxPooling2D((2,&amp;nbsp;2))) &lt;br /&gt;model.add(Flatten()) &lt;br /&gt;model.add(Dense(128,&amp;nbsp;activation='relu')) &lt;br /&gt;model.add(Dropout(0.5)) &lt;br /&gt;model.add(Dense(n_classes,&amp;nbsp;activation='softmax')) &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;3)&amp;nbsp;compile &lt;br /&gt;model.compile('adam',&amp;nbsp;loss='categorical_crossentropy',&amp;nbsp;metrics=['accuracy']) &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;4)&amp;nbsp;정지규칙 &lt;br /&gt;from&amp;nbsp;tensorflow.keras.callbacks&amp;nbsp;import&amp;nbsp;EarlyStopping &lt;br /&gt;es&amp;nbsp;=&amp;nbsp;EarlyStopping(monitor='val_loss',&amp;nbsp;patience=10,&amp;nbsp;restore_best_weights=True) &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;5)&amp;nbsp;학습 &lt;br /&gt;hist&amp;nbsp;=&amp;nbsp;model.fit(train_x,&amp;nbsp;train_y10,&amp;nbsp;validation_split=0.25, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;batch_size=10,&amp;nbsp;epochs=50000,&amp;nbsp;callbacks=[es]) &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;6)&amp;nbsp;평가 &lt;br /&gt;model.evaluate(train_x,&amp;nbsp;train_y10)[1]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;0.9393 &lt;br /&gt;model.evaluate(test_x,&amp;nbsp;test_y10)[1]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;0.7051 &lt;br /&gt;&lt;br /&gt;acc&amp;nbsp;=&amp;nbsp;hist.history['accuracy'] &lt;br /&gt;plt.plot(range(1,&amp;nbsp;len(acc)+1),&amp;nbsp;hist.history['accuracy'],&amp;nbsp;label='train') &lt;br /&gt;plt.plot(range(1,&amp;nbsp;len(acc)+1),&amp;nbsp;hist.history['val_accuracy'],&amp;nbsp;label='val') &lt;br /&gt;plt.legend() &lt;br /&gt;plt.xticks(range(1,&amp;nbsp;len(acc)+1)) &lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;587&quot; data-origin-height=&quot;434&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDPSZr/dJMcad3T9Mz/UukAZ90zk3mnzMvZ2nv9aK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDPSZr/dJMcad3T9Mz/UukAZ90zk3mnzMvZ2nv9aK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDPSZr/dJMcad3T9Mz/UukAZ90zk3mnzMvZ2nv9aK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDPSZr%2FdJMcad3T9Mz%2FUukAZ90zk3mnzMvZ2nv9aK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;587&quot; height=&quot;434&quot; data-origin-width=&quot;587&quot; data-origin-height=&quot;434&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-04 새로운 얼굴 사진 예측 함수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 데이터 경로에 다음 사진을 넣어준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;338&quot; data-origin-height=&quot;518&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bc7ovJ/dJMcaa7jpht/PD6CZwWji8E23xckZMw53k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bc7ovJ/dJMcaa7jpht/PD6CZwWji8E23xckZMw53k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bc7ovJ/dJMcaa7jpht/PD6CZwWji8E23xckZMw53k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbc7ovJ%2FdJMcaa7jpht%2FPD6CZwWji8E23xckZMw53k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;338&quot; height=&quot;518&quot; data-origin-width=&quot;338&quot; data-origin-height=&quot;518&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;from&amp;nbsp;PIL&amp;nbsp;import&amp;nbsp;Image &lt;br /&gt;import&amp;nbsp;numpy&amp;nbsp;as&amp;nbsp;np &lt;br /&gt;import&amp;nbsp;matplotlib.pyplot&amp;nbsp;as&amp;nbsp;plt&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;plt.rcParams['font.family']&amp;nbsp;=&amp;nbsp;'Malgun&amp;nbsp;Gothic'&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;Windows &lt;br /&gt;plt.rcParams['axes.unicode_minus']&amp;nbsp;=&amp;nbsp;False&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;마이너스&amp;nbsp;기호&amp;nbsp;깨짐&amp;nbsp;방지 &lt;br /&gt;&lt;br /&gt;def&amp;nbsp;f_predict_face(img_path,&amp;nbsp;model,&amp;nbsp;h,&amp;nbsp;w,&amp;nbsp;yname,&amp;nbsp;X_images,&amp;nbsp;y):&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;1)&amp;nbsp;이미지&amp;nbsp;로딩&amp;nbsp;및&amp;nbsp;흑백&amp;nbsp;변환 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;img&amp;nbsp;=&amp;nbsp;Image.open(img_path).convert('L')&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;흑백(그레이스케일)&amp;nbsp;변환 &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;2)&amp;nbsp;크기&amp;nbsp;맞추기&amp;nbsp;(학습&amp;nbsp;데이터와&amp;nbsp;동일한&amp;nbsp;h,&amp;nbsp;w) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;img&amp;nbsp;=&amp;nbsp;img.resize((w,&amp;nbsp;h))&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;PIL은&amp;nbsp;(width,&amp;nbsp;height)&amp;nbsp;순서 &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;3)&amp;nbsp;배열&amp;nbsp;변환&amp;nbsp;및&amp;nbsp;스케일링&amp;nbsp;(0~255&amp;nbsp;-&amp;gt;&amp;nbsp;0~1) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;img_arr&amp;nbsp;=&amp;nbsp;np.array(img)&amp;nbsp;/&amp;nbsp;255.0 &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;4)&amp;nbsp;CNN&amp;nbsp;입력&amp;nbsp;형태로&amp;nbsp;변환&amp;nbsp;(1,&amp;nbsp;h,&amp;nbsp;w,&amp;nbsp;1) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;img_input&amp;nbsp;=&amp;nbsp;img_arr.reshape((1,&amp;nbsp;h,&amp;nbsp;w,&amp;nbsp;1)) &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;5)&amp;nbsp;예측 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pred&amp;nbsp;=&amp;nbsp;model.predict(img_input) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pred_class&amp;nbsp;=&amp;nbsp;pred.argmax(axis=1)[0] &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pred_name&amp;nbsp;=&amp;nbsp;yname[pred_class] &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pred_prob&amp;nbsp;=&amp;nbsp;pred[0][pred_class] &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&quot;예측&amp;nbsp;인물:&amp;nbsp;{pred_name},&amp;nbsp;확률:&amp;nbsp;{pred_prob:.4f}&quot;) &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;6)&amp;nbsp;입력&amp;nbsp;사진&amp;nbsp;vs&amp;nbsp;예측된&amp;nbsp;클래스의&amp;nbsp;실제&amp;nbsp;사진&amp;nbsp;비교&amp;nbsp;출력 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;real_img&amp;nbsp;=&amp;nbsp;X_images[y&amp;nbsp;==&amp;nbsp;pred_class][0]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;예측&amp;nbsp;클래스의&amp;nbsp;실제&amp;nbsp;이미지&amp;nbsp;하나&amp;nbsp;추출 &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;fig,&amp;nbsp;ax&amp;nbsp;=&amp;nbsp;plt.subplots(1,&amp;nbsp;2) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ax[0].imshow(img_arr,&amp;nbsp;cmap='gray') &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ax[0].set_title('입력&amp;nbsp;사진') &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ax[0].axis('off') &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ax[1].imshow(real_img,&amp;nbsp;cmap='gray') &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ax[1].set_title(f'예측:&amp;nbsp;{pred_name}') &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ax[1].axis('off') &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;사용&amp;nbsp;예시 &lt;br /&gt;f_predict_face('image_sample.jpg',&amp;nbsp;model,&amp;nbsp;h,&amp;nbsp;w,&amp;nbsp;yname,&amp;nbsp;X_images,&amp;nbsp;y) &lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;531&quot; data-origin-height=&quot;355&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bF1jf0/dJMcagGsYNW/emJG9SQbiM8nmEHSC9mxk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bF1jf0/dJMcagGsYNW/emJG9SQbiM8nmEHSC9mxk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bF1jf0/dJMcagGsYNW/emJG9SQbiM8nmEHSC9mxk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbF1jf0%2FdJMcagGsYNW%2FemJG9SQbiM8nmEHSC9mxk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;531&quot; height=&quot;355&quot; data-origin-width=&quot;531&quot; data-origin-height=&quot;355&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1785209163504&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;f_predict_face('image_sample.jpg', model, h, w, yname, X_images, y)
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 60ms/step
예측 인물: Junichiro Koizumi, 확률: 0.7375&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-05&amp;nbsp;사전학습&amp;nbsp;모델(EfficientNetB0)&amp;nbsp;+&amp;nbsp;얼굴&amp;nbsp;데이터&amp;nbsp;전이학습 &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) 데이터 로딩 (컬러로 로드해야 사전학습 모델 입력에 맞음) &lt;br /&gt;from&amp;nbsp;sklearn.datasets&amp;nbsp;import&amp;nbsp;fetch_lfw_people &lt;br /&gt;import&amp;nbsp;numpy&amp;nbsp;as&amp;nbsp;np &lt;br /&gt;&lt;br /&gt;people&amp;nbsp;=&amp;nbsp;fetch_lfw_people(min_faces_per_person=50,&amp;nbsp;resize=0.7,&amp;nbsp;color=True) &lt;br /&gt;X_images&amp;nbsp;=&amp;nbsp;people['images']&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;(n,&amp;nbsp;h,&amp;nbsp;w,&amp;nbsp;3) &lt;br /&gt;y&amp;nbsp;=&amp;nbsp;people['target'] &lt;br /&gt;yname&amp;nbsp;=&amp;nbsp;people['target_names'] &lt;br /&gt;n_classes&amp;nbsp;=&amp;nbsp;len(yname) &lt;br /&gt;&lt;br /&gt;h,&amp;nbsp;w&amp;nbsp;=&amp;nbsp;X_images.shape[1],&amp;nbsp;X_images.shape[2] &lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style1&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;2) 데이터 분리 &lt;br /&gt;from&amp;nbsp;sklearn.model_selection&amp;nbsp;import&amp;nbsp;train_test_split &lt;br /&gt;train_x,&amp;nbsp;test_x,&amp;nbsp;train_y,&amp;nbsp;test_y&amp;nbsp;=&amp;nbsp;train_test_split(X_images,&amp;nbsp;y,&amp;nbsp;random_state=0,&amp;nbsp;stratify=y) &lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style1&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;3) 사전학습 모델 입력 크기(224x224)로 리사이즈 &lt;br /&gt;import&amp;nbsp;tensorflow&amp;nbsp;as&amp;nbsp;tf &lt;br /&gt;&lt;br /&gt;def&amp;nbsp;resize_batch(x,&amp;nbsp;size=(224,&amp;nbsp;224)): &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;x&amp;nbsp;=&amp;nbsp;tf.image.resize(x,&amp;nbsp;size) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;x.numpy() &lt;br /&gt;&lt;br /&gt;train_x_rs&amp;nbsp;=&amp;nbsp;resize_batch(train_x) &lt;br /&gt;test_x_rs&amp;nbsp;&amp;nbsp;=&amp;nbsp;resize_batch(test_x) &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;EfficientNet은&amp;nbsp;0~255&amp;nbsp;입력을&amp;nbsp;받아&amp;nbsp;내부적으로&amp;nbsp;전처리하므로&amp;nbsp;/255&amp;nbsp;하지&amp;nbsp;않음 &lt;br /&gt;#&amp;nbsp;(fetch_lfw_people은&amp;nbsp;0~1로&amp;nbsp;스케일링되어&amp;nbsp;있으므로&amp;nbsp;되돌려줌) &lt;br /&gt;train_x_rs&amp;nbsp;=&amp;nbsp;train_x_rs&amp;nbsp;*&amp;nbsp;255 &lt;br /&gt;test_x_rs&amp;nbsp;&amp;nbsp;=&amp;nbsp;test_x_rs&amp;nbsp;*&amp;nbsp;255 &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;원핫&amp;nbsp;인코딩 &lt;br /&gt;import&amp;nbsp;pandas&amp;nbsp;as&amp;nbsp;pd &lt;br /&gt;train_y10&amp;nbsp;=&amp;nbsp;pd.get_dummies(train_y).astype(int).values &lt;br /&gt;test_y10&amp;nbsp;&amp;nbsp;=&amp;nbsp;pd.get_dummies(test_y).astype(int).values &lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style1&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;4) 사전학습 모델 로딩 (특성 추출기로 사용, 가중치 고정) &lt;br /&gt;from&amp;nbsp;tensorflow.keras.applications&amp;nbsp;import&amp;nbsp;EfficientNetB0 &lt;br /&gt;from&amp;nbsp;tensorflow.keras.layers&amp;nbsp;import&amp;nbsp;Input,&amp;nbsp;Dense,&amp;nbsp;Dropout,&amp;nbsp;GlobalAveragePooling2D &lt;br /&gt;from&amp;nbsp;tensorflow.keras.models&amp;nbsp;import&amp;nbsp;Model &lt;br /&gt;&lt;br /&gt;base_model&amp;nbsp;=&amp;nbsp;EfficientNetB0(include_top=False,&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;ImageNet&amp;nbsp;1000개&amp;nbsp;클래스용&amp;nbsp;마지막&amp;nbsp;분류층을&amp;nbsp;빼고,&amp;nbsp;출력층을&amp;nbsp;새로&amp;nbsp;붙이겠다는&amp;nbsp;옵션 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;weights='imagenet',&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;이미지&amp;nbsp;분류&amp;nbsp;연구용으로&amp;nbsp;만든&amp;nbsp;대규모&amp;nbsp;데이터셋(1,000개&amp;nbsp;클래스,&amp;nbsp;약&amp;nbsp;120만&amp;nbsp;장) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;input_shape=(224,&amp;nbsp;224,&amp;nbsp;3))&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;ImageNet&amp;nbsp;학습&amp;nbsp;크기이므로&amp;nbsp;사용&amp;nbsp;권장(변경가능) &lt;br /&gt;base_model.trainable&amp;nbsp;=&amp;nbsp;False&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;사전학습&amp;nbsp;가중치&amp;nbsp;고정&amp;nbsp;(feature&amp;nbsp;extractor로만&amp;nbsp;사용) &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;모델&amp;nbsp;생성(base_model&amp;nbsp;자체가&amp;nbsp;이미&amp;nbsp;완성된&amp;nbsp;하나의&amp;nbsp;모델이므로&amp;nbsp;Sequential&amp;nbsp;방식&amp;nbsp;사용&amp;nbsp;불가&amp;nbsp;-&amp;gt;&amp;nbsp;Functional&amp;nbsp;API&amp;nbsp;방식&amp;nbsp;사용) &lt;br /&gt;#&amp;nbsp;Functional&amp;nbsp;API&amp;nbsp;:&amp;nbsp;각&amp;nbsp;층을&amp;nbsp;함수처럼&amp;nbsp;다루면서&amp;nbsp;입력과&amp;nbsp;출력을&amp;nbsp;직접&amp;nbsp;연결하는&amp;nbsp;방식 &lt;br /&gt;inputs&amp;nbsp;=&amp;nbsp;Input(shape=(224,&amp;nbsp;224,&amp;nbsp;3)) &lt;br /&gt;x&amp;nbsp;=&amp;nbsp;base_model(inputs,&amp;nbsp;training=False) &lt;br /&gt;x&amp;nbsp;=&amp;nbsp;GlobalAveragePooling2D()(x) &lt;br /&gt;x&amp;nbsp;=&amp;nbsp;Dense(128,&amp;nbsp;activation='relu')(x) &lt;br /&gt;x&amp;nbsp;=&amp;nbsp;Dropout(0.5)(x) &lt;br /&gt;outputs&amp;nbsp;=&amp;nbsp;Dense(n_classes,&amp;nbsp;activation='softmax')(x)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;얼굴&amp;nbsp;데이터&amp;nbsp;출력&amp;nbsp;층&amp;nbsp;연결 &lt;br /&gt;&lt;br /&gt;model&amp;nbsp;=&amp;nbsp;Model(inputs,&amp;nbsp;outputs) &lt;br /&gt;model.compile('adam',&amp;nbsp;loss='categorical_crossentropy',&amp;nbsp;metrics=['accuracy']) &lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style1&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;5) 학습 &lt;br /&gt;from&amp;nbsp;tensorflow.keras.callbacks&amp;nbsp;import&amp;nbsp;EarlyStopping &lt;br /&gt;es&amp;nbsp;=&amp;nbsp;EarlyStopping(monitor='val_loss',&amp;nbsp;patience=5,&amp;nbsp;restore_best_weights=True) &lt;br /&gt;&lt;br /&gt;hist&amp;nbsp;=&amp;nbsp;model.fit(train_x_rs,&amp;nbsp;train_y10,&amp;nbsp;validation_split=0.25, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;batch_size=16,&amp;nbsp;epochs=50,&amp;nbsp;callbacks=[es]) &lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style1&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;6) 평가 &lt;br /&gt;model.evaluate(train_x_rs,&amp;nbsp;train_y10)[1]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;0.9820 &lt;br /&gt;model.evaluate(test_x_rs,&amp;nbsp;test_y10)[1]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;0.9102&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;03 쇼핑몰 후기 감성 분석&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;import&amp;nbsp;pandas&amp;nbsp;as&amp;nbsp;pd &lt;br /&gt;df&amp;nbsp;=&amp;nbsp;pd.read_table('shopping_ratings_total.txt',&amp;nbsp;names&amp;nbsp;=&amp;nbsp;['ratings',&amp;nbsp;'reviews']) &lt;br /&gt;df.head()&lt;/p&gt;
&lt;pre id=&quot;code_1785216949164&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;df.head()
Out[328]: 
   ratings                                            reviews
0        5                                            배공빠르고 굿
1        2                      택배가 엉망이네용 저희집 밑에층에 말도없이 놔두고가고
2        5  아주좋아요 바지 정말 좋아서2개 더 구매했어요 이가격에 대박입니다. 바느질이 조금 ...
3        2  선물용으로 빨리 받아서 전달했어야 하는 상품이었는데 머그컵만 와서 당황했습니다. 전...
4        5                  민트색상 예뻐요. 옆 손잡이는 거는 용도로도 사용되네요 ㅎㅎ
...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;df.shape&lt;/p&gt;
&lt;pre id=&quot;code_1785217421147&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;df.shape
Out[329]: (200000, 2)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2)&amp;nbsp;target&amp;nbsp;변수&amp;nbsp;변환&amp;nbsp;(평점&amp;nbsp;-&amp;gt;&amp;nbsp;이진데이터) &lt;br /&gt;import&amp;nbsp;numpy&amp;nbsp;as&amp;nbsp;np &lt;br /&gt;np.unique(df['ratings'])&lt;/p&gt;
&lt;pre id=&quot;code_1785220829836&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;np.unique(df['ratings'])
Out[6]: array([1, 2, 4, 5])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3)&amp;nbsp;샘플링 &lt;br /&gt;#&amp;nbsp;실제&amp;nbsp;분석&amp;nbsp;과정에서는&amp;nbsp;생략한다. &lt;br /&gt;#&amp;nbsp;금일&amp;nbsp;실습에서는&amp;nbsp;20만건&amp;nbsp;학습이&amp;nbsp;불가능한&amp;nbsp;상황이기&amp;nbsp;때문에&amp;nbsp;3만건만&amp;nbsp;우선&amp;nbsp;랜덤샘플링을&amp;nbsp;진행한다. &lt;br /&gt;&lt;br /&gt;df&amp;nbsp;=&amp;nbsp;df.sample(n=30000,&amp;nbsp;random_state=0).reset_index(drop=True)&amp;nbsp;#&amp;nbsp;랜덤&amp;nbsp;샘플링이기&amp;nbsp;때문에&amp;nbsp;index를&amp;nbsp;초기화한다. &lt;br /&gt;&lt;br /&gt;4) 데이터 분리 (train/test) &lt;br /&gt;from&amp;nbsp;sklearn.model_selection&amp;nbsp;import&amp;nbsp;train_test_split &lt;br /&gt;train_test_aplit(df['reviews'],&amp;nbsp;df['ratings'],&amp;nbsp;random_state=0,&amp;nbsp;stratify=df['ratings']) &lt;br /&gt;&lt;br /&gt;5) 전처리 (정제 -&amp;gt; 형태소 분석) &lt;br /&gt;#&amp;nbsp;pip&amp;nbsp;install&amp;nbsp;konlpy &lt;br /&gt;import&amp;nbsp;konlpy.tag &lt;br /&gt;Okt&amp;nbsp;=&amp;nbsp;konlpy.tag.Okt()&amp;nbsp;&amp;nbsp;#&amp;nbsp;RuntimeError:&amp;nbsp;Java&amp;nbsp;versioin&amp;nbsp;too&amp;nbsp;old.&amp;nbsp;Java&amp;nbsp;9&amp;nbsp;or&amp;nbsp;later&amp;nbsp;is&amp;nbsp;required &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;cmd에서&amp;nbsp;java&amp;nbsp;-version &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;**&amp;nbsp;java&amp;nbsp;최신버전&amp;nbsp;설치 &lt;br /&gt;#&amp;nbsp;&lt;a href=&quot;https://www.oracle.com/java/technologies/downloads/#jdk26-windows&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.oracle.com/java/technologies/downloads/#jdk26-windows&lt;/a&gt;&lt;br /&gt;#&amp;nbsp;설치&amp;nbsp;후&amp;nbsp;명령&amp;nbsp;프롬포트는&amp;nbsp;다시&amp;nbsp;실행한&amp;nbsp;다음에&amp;nbsp;버전확인이&amp;nbsp;적용된다. &lt;br /&gt;#&amp;nbsp;이후&amp;nbsp;path&amp;nbsp;등록을&amp;nbsp;진행해야&amp;nbsp;한다.&amp;nbsp;(시스템&amp;nbsp;환경&amp;nbsp;변수&amp;nbsp;편집&amp;nbsp;-&amp;nbsp;환경&amp;nbsp;변수&amp;nbsp;-&amp;nbsp;path&amp;nbsp;-&amp;nbsp;다운로드&amp;nbsp;받은&amp;nbsp;C:\Program&amp;nbsp;Files\Java\jdk-26.0.2\bin&amp;nbsp;추가&amp;nbsp;후&amp;nbsp;확인) &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>아이티윌_데이터 분석 55기/강의내용_딥러닝</category>
      <author>ecosso</author>
      <guid isPermaLink="true">https://ecosso.tistory.com/146</guid>
      <comments>https://ecosso.tistory.com/146#entry146comment</comments>
      <pubDate>Tue, 28 Jul 2026 16:06:53 +0900</pubDate>
    </item>
    <item>
      <title>#3 3일차_ANN(다중 분류), 이미지 분석(PCA+KNN, ANN, CNN)</title>
      <link>https://ecosso.tistory.com/144</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;01 ANN - 다중 분류&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;02 PCA + KNN 모델을 이용한 이미지 분석&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;03 ANN 모델을 사용한 이미지 분석&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;04 CNN 모델을 사용한 이미지 분석&lt;/b&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;01&amp;nbsp; ann - 다중 분류&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) 데이터 로딩&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;from&amp;nbsp;sklearn.datasets&amp;nbsp;import&amp;nbsp;load_iris &lt;br /&gt;iris&amp;nbsp;=&amp;nbsp;load_iris() &lt;br /&gt;X,&amp;nbsp;y&amp;nbsp;=&amp;nbsp;iris.data,&amp;nbsp;iris.target &lt;br /&gt;X.shape &lt;br /&gt;y.shape &lt;/p&gt;
&lt;pre id=&quot;code_1785114447848&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;X.shape
Out[4]: (150, 4)

y.shape
Out[5]: (150,)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;#&amp;nbsp;target&amp;nbsp;변수&amp;nbsp;원핫&amp;nbsp;인코딩&amp;nbsp;(3개&amp;nbsp;클래스) &lt;br /&gt;import&amp;nbsp;pandas&amp;nbsp;as&amp;nbsp;pd &lt;br /&gt;y3&amp;nbsp;=&amp;nbsp;pd.get_dummies(y,&amp;nbsp;prefix='Y').astype('int')&lt;/p&gt;
&lt;pre id=&quot;code_1785114457447&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pd.get_dummies(y, prefix='Y').astype('int')
Out[9]: 
     Y_0  Y_1  Y_2
0      1    0    0
1      1    0    0
2      1    0    0
3      1    0    0
4      1    0    0
..   ...  ...  ...
145    0    0    1
146    0    0    1
147    0    0    1
148    0    0    1
149    0    0    1

[150 rows x 3 columns]&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) 스케일링&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;from&amp;nbsp;sklearn.preprocessing&amp;nbsp;import&amp;nbsp;StandardScaler,&amp;nbsp;MinMaxScaler &lt;br /&gt;m_sc&amp;nbsp;=&amp;nbsp;MinMaxScaler() &lt;br /&gt;X_sc&amp;nbsp;=&amp;nbsp;m_sc.fit_transform(X)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3) 데이터 분리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;from&amp;nbsp;sklearn.model_selection&amp;nbsp;import&amp;nbsp;train_test_split &lt;br /&gt;train_x,&amp;nbsp;test_x,&amp;nbsp;train_y,&amp;nbsp;test_y&amp;nbsp;=&amp;nbsp;train_test_split(X,&amp;nbsp;y,&amp;nbsp;random_state=0,&amp;nbsp;stratify=y)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4) 모델링&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;1)&amp;nbsp;seed&amp;nbsp;고정 &lt;br /&gt;import&amp;nbsp;numpy&amp;nbsp;as&amp;nbsp;np &lt;br /&gt;import&amp;nbsp;tensorflow&amp;nbsp;as&amp;nbsp;tf &lt;br /&gt;seed&amp;nbsp;=&amp;nbsp;0 &lt;br /&gt;np.random.seed(seed) &lt;br /&gt;tf.random.set_seed(seed) &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;2)&amp;nbsp;모델&amp;nbsp;정의 &lt;br /&gt;from&amp;nbsp;tensorflow.keras.layers&amp;nbsp;import&amp;nbsp;Input &lt;br /&gt;from&amp;nbsp;keras&amp;nbsp;import&amp;nbsp;Sequential &lt;br /&gt;from&amp;nbsp;keras.layers&amp;nbsp;import&amp;nbsp;Dense &lt;br /&gt;&lt;br /&gt;model&amp;nbsp;=&amp;nbsp;Sequential() &lt;br /&gt;model.add(Input(shape=(train_x.shape[1],&amp;nbsp;)))&amp;nbsp;&amp;nbsp; &lt;br /&gt;model.add(Dense(2,&amp;nbsp;activation='relu')) &lt;br /&gt;model.add(Dense(1,&amp;nbsp;activation='sigmoid'))&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;3)&amp;nbsp;오차함수,&amp;nbsp;최적화&amp;nbsp;결정 &lt;br /&gt;model.compile(optimizer='adam',&amp;nbsp;loss='categorical_crossentropy',&amp;nbsp;metrics=['accuracy']) &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;4)&amp;nbsp;early&amp;nbsp;stopping&amp;nbsp;rule &lt;br /&gt;from&amp;nbsp;tensorflow.keras.callbacks&amp;nbsp;import&amp;nbsp;EarlyStopping &lt;br /&gt;es&amp;nbsp;=&amp;nbsp;EarlyStopping(monitor='val_loss',&amp;nbsp;patience=10,&amp;nbsp;restore_best_weights&amp;nbsp;=&amp;nbsp;True)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;#&amp;nbsp;5)&amp;nbsp;학습 &lt;br /&gt;hist&amp;nbsp;=&amp;nbsp;model.fit(train_x,&amp;nbsp;train_y,&amp;nbsp;validation_split=0.25,&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;batch_size = 10, epochs=50000, callbacks=[es])&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1785116042864&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Epoch 1/50000
C:\Users\itwill\anaconda3\Lib\site-packages\keras\src\losses\losses.py:34: SyntaxWarning: In loss categorical_crossentropy, expected y_pred.shape to be (batch_size, num_classes) with num_classes &amp;gt; 1. Received: y_pred.shape=(None, 1). Consider using 'binary_crossentropy' if you only have 2 classes.
  return self.fn(y_true, y_pred, **self._fn_kwargs)
9/9 ━━━━━━━━━━━━━━━━━━━━ 1s 23ms/step - accuracy: 0.3571 - loss: 0.0000e+00 - val_accuracy: 0.2500 - val_loss: 0.0000e+00
Epoch 2/50000
9/9 ━━━━━━━━━━━━━━━━━━━━ 0s 8ms/step - accuracy: 0.3571 - loss: 0.0000e+00 - val_accuracy: 0.2500 - val_loss: 0.0000e+00
Epoch 3/50000
9/9 ━━━━━━━━━━━━━━━━━━━━ 0s 8ms/step - accuracy: 0.3571 - loss: 0.0000e+00 - val_accuracy: 0.2500 - val_loss: 0.0000e+00
Epoch 4/50000
9/9 ━━━━━━━━━━━━━━━━━━━━ 0s 7ms/step - accuracy: 0.3571 - loss: 0.0000e+00 - val_accuracy: 0.2500 - val_loss: 0.0000e+00
Epoch 5/50000
9/9 ━━━━━━━━━━━━━━━━━━━━ 0s 7ms/step - accuracy: 0.3571 - loss: 0.0000e+00 - val_accuracy: 0.2500 - val_loss: 0.0000e+00
Epoch 6/50000
9/9 ━━━━━━━━━━━━━━━━━━━━ 0s 8ms/step - accuracy: 0.3571 - loss: 0.0000e+00 - val_accuracy: 0.2500 - val_loss: 0.0000e+00
Epoch 7/50000
9/9 ━━━━━━━━━━━━━━━━━━━━ 0s 8ms/step - accuracy: 0.3571 - loss: 0.0000e+00 - val_accuracy: 0.2500 - val_loss: 0.0000e+00
Epoch 8/50000
9/9 ━━━━━━━━━━━━━━━━━━━━ 0s 8ms/step - accuracy: 0.3571 - loss: 0.0000e+00 - val_accuracy: 0.2500 - val_loss: 0.0000e+00
Epoch 9/50000
9/9 ━━━━━━━━━━━━━━━━━━━━ 0s 7ms/step - accuracy: 0.3571 - loss: 0.0000e+00 - val_accuracy: 0.2500 - val_loss: 0.0000e+00
Epoch 10/50000
9/9 ━━━━━━━━━━━━━━━━━━━━ 0s 7ms/step - accuracy: 0.3571 - loss: 0.0000e+00 - val_accuracy: 0.2500 - val_loss: 0.0000e+00
Epoch 11/50000
9/9 ━━━━━━━━━━━━━━━━━━━━ 0s 7ms/step - accuracy: 0.3571 - loss: 0.0000e+00 - val_accuracy: 0.2500 - val_loss: 0.0000e+00&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;1.&amp;nbsp;데이터&amp;nbsp;로딩 &lt;br /&gt;from&amp;nbsp;sklearn.datasets&amp;nbsp;import&amp;nbsp;load_iris &lt;br /&gt;iris&amp;nbsp;=&amp;nbsp;load_iris() &lt;br /&gt;X,&amp;nbsp;y&amp;nbsp;=&amp;nbsp;iris.data,&amp;nbsp;iris.target &lt;br /&gt;X.shape &lt;br /&gt;y.shape &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;target&amp;nbsp;변수&amp;nbsp;원핫&amp;nbsp;인코딩(3개&amp;nbsp;클래스) &lt;br /&gt;import&amp;nbsp;pandas&amp;nbsp;as&amp;nbsp;pd &lt;br /&gt;y3&amp;nbsp;=&amp;nbsp;pd.get_dummies(y,&amp;nbsp;prefix='Y').astype('int') &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;2.&amp;nbsp;스케일링 &lt;br /&gt;from&amp;nbsp;sklearn.preprocessing&amp;nbsp;import&amp;nbsp;StandardScaler,&amp;nbsp;MinMaxScaler &lt;br /&gt;m_sc&amp;nbsp;=&amp;nbsp;MinMaxScaler() &lt;br /&gt;X_sc&amp;nbsp;=&amp;nbsp;m_sc.fit_transform(X) &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;3.&amp;nbsp;데이터&amp;nbsp;분리 &lt;br /&gt;from&amp;nbsp;sklearn.model_selection&amp;nbsp;import&amp;nbsp;train_test_split &lt;br /&gt;train_x,&amp;nbsp;test_x,&amp;nbsp;train_y,&amp;nbsp;test_y,&amp;nbsp;&lt;span style=&quot;background-color: #f3c000;&quot;&gt;train_y3,&amp;nbsp;test_y3&lt;/span&gt;&amp;nbsp;=&amp;nbsp;train_test_split(X,&amp;nbsp;y,&amp;nbsp;y3,&amp;nbsp;random_state=0,&amp;nbsp;stratify=y) &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;4.&amp;nbsp;모델링 &lt;br /&gt;#&amp;nbsp;1)&amp;nbsp;seed&amp;nbsp;고정 &lt;br /&gt;import&amp;nbsp;numpy&amp;nbsp;as&amp;nbsp;np &lt;br /&gt;import&amp;nbsp;tensorflow&amp;nbsp;as&amp;nbsp;tf &lt;br /&gt;seed&amp;nbsp;=&amp;nbsp;0 &lt;br /&gt;np.random.seed(seed) &lt;br /&gt;tf.random.set_seed(seed) &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;2)&amp;nbsp;모델&amp;nbsp;정의 &lt;br /&gt;from&amp;nbsp;tensorflow.keras.layers&amp;nbsp;import&amp;nbsp;Input &lt;br /&gt;from&amp;nbsp;keras&amp;nbsp;import&amp;nbsp;Sequential &lt;br /&gt;from&amp;nbsp;keras.layers&amp;nbsp;import&amp;nbsp;Dense &lt;br /&gt;&lt;br /&gt;model&amp;nbsp;=&amp;nbsp;Sequential() &lt;br /&gt;model.add(Input(shape=(train_x.shape[1],&amp;nbsp;)))&amp;nbsp;&amp;nbsp; &lt;br /&gt;model.add(Dense(2,&amp;nbsp;activation='relu')) &lt;br /&gt;model.add(Dense(&lt;span style=&quot;background-color: #f3c000;&quot;&gt;3&lt;/span&gt;,&amp;nbsp;activation='softmax'))&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;다중분류일때&amp;nbsp;출력층&amp;nbsp;활성함수는&amp;nbsp;sigmoid&amp;nbsp;또는&amp;nbsp;softmax&amp;nbsp;적합&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;3)&amp;nbsp;오차함수,&amp;nbsp;최적화&amp;nbsp;결정 &lt;br /&gt;model.compile(optimizer='adam',&amp;nbsp;loss='categorical_crossentropy',&amp;nbsp;metrics=['accuracy']) &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;4)&amp;nbsp;early&amp;nbsp;stopping&amp;nbsp;rule &lt;br /&gt;from&amp;nbsp;tensorflow.keras.callbacks&amp;nbsp;import&amp;nbsp;EarlyStopping &lt;br /&gt;es&amp;nbsp;=&amp;nbsp;EarlyStopping(monitor='val_loss',&amp;nbsp;patience=10,&amp;nbsp;restore_best_weights&amp;nbsp;=&amp;nbsp;True)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;#&amp;nbsp;5)&amp;nbsp;학습 &lt;br /&gt;hist&amp;nbsp;=&amp;nbsp;model.fit(train_x,&amp;nbsp;&lt;span style=&quot;background-color: #f3c000;&quot;&gt;train_y3&lt;/span&gt;,&amp;nbsp;validation_split=0.25,&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;batch_size&amp;nbsp;=&amp;nbsp;10,&amp;nbsp;epochs=50000,&amp;nbsp;callbacks=[es])&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;02 PCA + KNN 모델을 이용한 이미지 분석&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이미지는 비정형데이터이므로, 우선 분석을 위해서는 정형데이터처럼 컴퓨터가 인식가능한 형태로 변형되어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이미지 데이터는 픽셀로 나누어져 있으며 각 픽셀은 RGB 코드에 의해 0~255의 값을 각각 가지게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;예를 들어 4 X 5의 해상도를 가지는 파일의 경우, 20개의 픽셀을 가지게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;즉, 20개의 열이 생성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;PCA 또는 KNN과 같은 머신러닝 모델은 일반적으로 2차원 표 형태의 입력을 사용하기 때문에 위와 같은 과정을 통해 이미지 한 장의 높이 X 너비 배열을 하나의 긴 벡터로 펼치는 작업이 필요하다. 이를 flatten이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;즉, 이미지 한 장이 1차원 벡터로 펼쳐지며 전체 이미지 데이터셋이 2차원 표가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이미지를 flatten하는 과정에서 픽셀 하나하나가 변수가 되므로 해상도에 따라 변수의 수가 매우 많아지게 되는데, 이 과정에서 계산량 증가, 거리 계산의 의미가 약해지는 차원의 저주 등 여러가지 문제가 발생하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;따라서 PCA를 통해 데이터의 분산을 최대한 많이 설명할 수 있는 새로운 축인 주성분을 만들게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이는 여러 픽셀값을 선형적으로 조합하여 이미지 사이의 주요 변화 패턴을 나타내는 축을 찾는 과정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;=&amp;gt; PCA는 가장 큰 분산을 설명하는 주성분을 만들어줌으로써,&amp;nbsp;특정 픽셀의 변동이 심한 사진의 유사성을 판별/분류할 수 있게 도와준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;PCA로 차원을 축소한 이후에는 각 이미지가 몇 개의 주성분 점수로 표현되며, KNN을 통해 새로운 이미지와 기존 학습 이미지 사이에서 가까운 학습 이미지들을 찾아 주변 이웃이 가장 많이 속한 클래스로 분류한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그러나 차원 압축 / 분류 등을 통해 원래 이미지가 가지고 있던 픽셀들의 신호가 약해지는 경향을 보완하기 위하여 이 neural network를 통한 학습/분류가 이후에 발전하게 된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;1)&amp;nbsp;데이터&amp;nbsp;로딩&amp;nbsp;(RGB로&amp;nbsp;변환된&amp;nbsp;데이터) &lt;br /&gt;from&amp;nbsp;tensorflow.keras.datasets&amp;nbsp;import&amp;nbsp;mnist &lt;br /&gt;(train_x, train_y), (test_x, test_y) = mnist.load_data() # mnist는 train / test가 사전에 나누어져 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1785118246128&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(train_x, train_y), (test_x, test_y) = mnist.load_data()
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz
11490434/11490434 ━━━━━━━━━━━━━━━━━━━━ 1s 0us/step&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;train_x.shape&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 층 6만개, 28 X 28의 해상도&lt;/p&gt;
&lt;pre id=&quot;code_1785118272920&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;train_x.shape
Out[132]: (60000, 28, 28)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;train_y&lt;/p&gt;
&lt;pre id=&quot;code_1785118321864&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;train_y
Out[134]: array([5, 0, 4, ..., 5, 6, 8], shape=(60000,), dtype=uint8)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;import&amp;nbsp;numpy&amp;nbsp;as&amp;nbsp;np &lt;br /&gt;np.unique(train_y)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 숫자 손글씨이기 때문에 0~9의 값을 가진다.&lt;/p&gt;
&lt;pre id=&quot;code_1785118356536&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;np.unique(train_y)
Out[136]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=uint8)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 데이터 확인&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;import&amp;nbsp;matplotlib.pyplot&amp;nbsp;as&amp;nbsp;plt&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 사진의 값을 확인해본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;28 X 28 의 해상도를 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;train_x[0].shape&lt;/p&gt;
&lt;pre id=&quot;code_1785118438760&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;train_x[0].shape
Out[138]: (28, 28)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미 RGB 변환이 완료되었기에 2차원의 형태로 출력된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;train_x[0]&lt;/p&gt;
&lt;pre id=&quot;code_1785118398496&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;train_x[0]
Out[137]: 
array([[  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
          0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
          0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
          0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
          0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
          0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
          0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
          0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
          0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
          0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
          0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   3,
         18,  18,  18, 126, 136, 175,  26, 166, 255, 247, 127,   0,   0,
          0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0,  30,  36,  94, 154, 170,
        253, 253, 253, 253, 253, 225, 172, 253, 242, 195,  64,   0,   0,
          0,   0],
       [  0,   0,   0,   0,   0,   0,   0,  49, 238, 253, 253, 253, 253,
        253, 253, 253, 253, 251,  93,  82,  82,  56,  39,   0,   0,   0,
          0,   0],
       [  0,   0,   0,   0,   0,   0,   0,  18, 219, 253, 253, 253, 253,
        253, 198, 182, 247, 241,   0,   0,   0,   0,   0,   0,   0,   0,
          0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0,  80, 156, 107, 253, 253,
        205,  11,   0,  43, 154,   0,   0,   0,   0,   0,   0,   0,   0,
          0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0,   0,  14,   1, 154, 253,
         90,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
          0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 139, 253,
        190,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
          0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,  11, 190,
        253,  70,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
          0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,  35,
        241, 225, 160, 108,   1,   0,   0,   0,   0,   0,   0,   0,   0,
          0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
         81, 240, 253, 253, 119,  25,   0,   0,   0,   0,   0,   0,   0,
          0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
          0,  45, 186, 253, 253, 150,  27,   0,   0,   0,   0,   0,   0,
          0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
          0,   0,  16,  93, 252, 253, 187,   0,   0,   0,   0,   0,   0,
          0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
          0,   0,   0,   0, 249, 253, 249,  64,   0,   0,   0,   0,   0,
          0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
          0,  46, 130, 183, 253, 253, 207,   2,   0,   0,   0,   0,   0,
          0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,  39,
        148, 229, 253, 253, 253, 250, 182,   0,   0,   0,   0,   0,   0,
          0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,  24, 114, 221,
        253, 253, 253, 253, 201,  78,   0,   0,   0,   0,   0,   0,   0,
          0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0,  23,  66, 213, 253, 253,
        253, 253, 198,  81,   2,   0,   0,   0,   0,   0,   0,   0,   0,
          0,   0],
       [  0,   0,   0,   0,   0,   0,  18, 171, 219, 253, 253, 253, 253,
        195,  80,   9,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
          0,   0],
       [  0,   0,   0,   0,  55, 172, 226, 253, 253, 253, 253, 244, 133,
         11,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
          0,   0],
       [  0,   0,   0,   0, 136, 253, 253, 253, 212, 135, 132,  16,   0,
          0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
          0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
          0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
          0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
          0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
          0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
          0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
          0,   0]], dtype=uint8)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;plt.imshow(train_x[0])&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;447&quot; data-origin-height=&quot;423&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wjVPM/dJMcaiRKeIA/1kxiuke1kkW47BRml2cOUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wjVPM/dJMcaiRKeIA/1kxiuke1kkW47BRml2cOUk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wjVPM/dJMcaiRKeIA/1kxiuke1kkW47BRml2cOUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwjVPM%2FdJMcaiRKeIA%2F1kxiuke1kkW47BRml2cOUk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;447&quot; height=&quot;423&quot; data-origin-width=&quot;447&quot; data-origin-height=&quot;423&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원본 데이터를 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 위 이미지의 실제 정답 찾기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;train_y[0]&lt;/p&gt;
&lt;pre id=&quot;code_1785118768040&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;train_y[0]
Out[142]: np.uint8(5)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) 데이터 변환&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;train_x.reshape(60000,&amp;nbsp;-1)&amp;nbsp;#&amp;nbsp;28&amp;nbsp;*&amp;nbsp;28&amp;nbsp;로&amp;nbsp;펼쳐짐&amp;nbsp;(flatten)&lt;/p&gt;
&lt;pre id=&quot;code_1785119725818&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;train_x.reshape(60000, -1)
Out[143]: 
array([[0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0],
       ...,
       [0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0]], shape=(60000, 784), dtype=uint8)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;train_x.reshape(60000, -1) / 255 # 0~255 =&amp;gt; 0~1의 범위로 변환&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MinMaxScaling과 같은 scaling 효과를 낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;train_x_sc = train_x.reshape(60000, -1) / 255&lt;br /&gt;test_x_sc = test_x.reshape(10000, -1) / 255 # test는 1만건&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3) 모델링&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;(1)&amp;nbsp;knn &lt;br /&gt;from&amp;nbsp;sklearn.neighbors&amp;nbsp;import&amp;nbsp;KNeighborsClassifier &lt;br /&gt;m_knn&amp;nbsp;=&amp;nbsp;KNeighborsClassifier() &lt;br /&gt;m_knn.fit(train_x_sc,&amp;nbsp;train_y) &lt;br /&gt;m_knn.score(train_x_sc,&amp;nbsp;train_y) &lt;br /&gt;m_knn.score(test_x_sc,&amp;nbsp;test_y)&lt;/p&gt;
&lt;pre id=&quot;code_1785120041569&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;m_knn.score(train_x_sc, train_y)
Out[151]: 0.9819166666666667

m_knn.score(test_x_sc, test_y)
Out[152]: 0.9688&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;(2)&amp;nbsp;pca&amp;nbsp;+&amp;nbsp;knn &lt;br /&gt;from&amp;nbsp;sklearn.neighbors&amp;nbsp;import&amp;nbsp;KNeighborsClassifier &lt;br /&gt;from&amp;nbsp;sklearn.decomposition&amp;nbsp;import&amp;nbsp;PCA &lt;br /&gt;from&amp;nbsp;sklearn.pipeline&amp;nbsp;import&amp;nbsp;make_pipeline &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;스케일링은&amp;nbsp;아까&amp;nbsp;수행했기&amp;nbsp;때문에&amp;nbsp;생략한다. &lt;br /&gt;pipe&amp;nbsp;=&amp;nbsp;make_pipeline(PCA(100),&amp;nbsp;KNeighborsClassifier(5)) &lt;br /&gt;pipe.fit(train_x_sc,&amp;nbsp;train_y) &lt;br /&gt;pipe.score(train_x_sc,&amp;nbsp;train_y) &lt;br /&gt;pipe.score(test_x_sc,&amp;nbsp;test_y)&lt;/p&gt;
&lt;pre id=&quot;code_1785120532865&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pipe.score(train_x_sc, train_y)
Out[159]: 0.9840333333333333

pipe.score(test_x_sc, test_y)
Out[160]: 0.9727&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;knn만 사용하는 것보다 pca + knn 을 같이 수행한 경우 score가 조금 더 향상되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;4)&amp;nbsp;예측&amp;nbsp;실패한&amp;nbsp;데이터&amp;nbsp;확인 &lt;br /&gt;pre_te&amp;nbsp;=&amp;nbsp;pipe.predict(test_x_sc)&lt;/p&gt;
&lt;pre id=&quot;code_1785120766753&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pre_te
Out[162]: array([7, 2, 1, ..., 4, 5, 6], shape=(10000,), dtype=uint8)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;pre_te&amp;nbsp;!=&amp;nbsp;test_y&lt;/p&gt;
&lt;pre id=&quot;code_1785120773769&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pre_te != test_y
Out[163]: array([False, False, False, ..., False, False, False], shape=(10000,))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;test_fail&amp;nbsp;=&amp;nbsp;test_x[pre_te&amp;nbsp;!=&amp;nbsp;test_y,&amp;nbsp;:,&amp;nbsp;:]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 10,000개 중 273개를 예측 실패하였음을 확인할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1785120902609&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;test_x[pre_te != test_y, :, :]
Out[165]: 
array([[[0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        ...,
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0]],

       [[0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        ...,
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0]],

       [[0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        ...,
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0]],

       ...,

       [[0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        ...,
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0]],

       [[0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        ...,
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0]],

       [[0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        ...,
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0]]], shape=(273, 28, 28), dtype=uint8)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;y 역시 틀린 값만 추출해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;y_true_fail&amp;nbsp;=&amp;nbsp;test_y[pre_te&amp;nbsp;!=&amp;nbsp;test_y] &lt;br /&gt;y_pre_fail&amp;nbsp;=&amp;nbsp;pre_te[pre_te&amp;nbsp;!=&amp;nbsp;test_y]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;plt.imshow(test_fail[0])&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;455&quot; data-origin-height=&quot;432&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wfbhz/dJMcafAIxjb/u83q2GJKAzZDFUEOuwn4CK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wfbhz/dJMcafAIxjb/u83q2GJKAzZDFUEOuwn4CK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wfbhz/dJMcafAIxjb/u83q2GJKAzZDFUEOuwn4CK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fwfbhz%2FdJMcafAIxjb%2Fu83q2GJKAzZDFUEOuwn4CK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;455&quot; height=&quot;432&quot; data-origin-width=&quot;455&quot; data-origin-height=&quot;432&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;y_true_fail[0] &lt;br /&gt;y_pre_fail[0]&lt;/p&gt;
&lt;pre id=&quot;code_1785121105433&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;y_true_fail[0]
Out[171]: np.uint8(4)

y_pre_fail[0]
Out[172]: np.uint8(9)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제값은 4, 예측값은 7로 예측실패하였음을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;plt.imshow(test_fail[1])&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;422&quot; data-origin-height=&quot;424&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rWgZp/dJMcafnentt/hZkh6XNAe2txZjKHH4lg01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rWgZp/dJMcafnentt/hZkh6XNAe2txZjKHH4lg01/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rWgZp/dJMcafnentt/hZkh6XNAe2txZjKHH4lg01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrWgZp%2FdJMcafnentt%2FhZkh6XNAe2txZjKHH4lg01%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;422&quot; height=&quot;424&quot; data-origin-width=&quot;422&quot; data-origin-height=&quot;424&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;y_true_fail[1] &lt;br /&gt;y_pre_fail[1]&lt;/p&gt;
&lt;pre id=&quot;code_1785121146081&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;y_true_fail[1]
Out[174]: np.uint8(4)

y_pre_fail[1]
Out[175]: np.uint8(0)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;여러&amp;nbsp;그래프를&amp;nbsp;동시에&amp;nbsp;시각화 &lt;br /&gt;fig,&amp;nbsp;ax&amp;nbsp;=&amp;nbsp;plt.subplots(2,5) &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;i는&amp;nbsp;y축&amp;nbsp;좌표 &lt;br /&gt;for&amp;nbsp;i&amp;nbsp;in&amp;nbsp;range(0,10)&amp;nbsp;: &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;i&amp;nbsp;&amp;lt;=&amp;nbsp;4&amp;nbsp;: &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ax[0,i].imshow(test_fail[i]) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ax[0,i].set_title('true&amp;nbsp;:&amp;nbsp;%s,&amp;nbsp;pre&amp;nbsp;:&amp;nbsp;%s'&amp;nbsp;%(y_true_fail[i],&amp;nbsp;y_pre_fail[i])) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else&amp;nbsp;:&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ax[1,i-5].imshow(test_fail[i]) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ax[1,i-5].set_title('true&amp;nbsp;:&amp;nbsp;%s,&amp;nbsp;pre&amp;nbsp;:&amp;nbsp;%s'&amp;nbsp;%(y_true_fail[i],&amp;nbsp;y_pre_fail[i]))&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1057&quot; data-origin-height=&quot;471&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/S920M/dJMcadv0qPO/smZ9KLMSuHrkOa10VSUEm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/S920M/dJMcadv0qPO/smZ9KLMSuHrkOa10VSUEm0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/S920M/dJMcadv0qPO/smZ9KLMSuHrkOa10VSUEm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FS920M%2FdJMcadv0qPO%2FsmZ9KLMSuHrkOa10VSUEm0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1057&quot; height=&quot;471&quot; data-origin-width=&quot;1057&quot; data-origin-height=&quot;471&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;03 ANN 모델을 사용한 이미지 분석&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;1.&amp;nbsp;데이터&amp;nbsp;로딩(RGB로&amp;nbsp;변환된&amp;nbsp;데이터) &lt;br /&gt;from&amp;nbsp;tensorflow.keras.datasets&amp;nbsp;import&amp;nbsp;mnist &lt;br /&gt;import&amp;nbsp;numpy&amp;nbsp;as&amp;nbsp;np &lt;br /&gt;&lt;br /&gt;(train_x,&amp;nbsp;train_y),&amp;nbsp;(test_x,&amp;nbsp;test_y)&amp;nbsp;=&amp;nbsp;mnist.load_data() &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;2.&amp;nbsp;데이터&amp;nbsp;변환 &lt;br /&gt;train_x_sc&amp;nbsp;=&amp;nbsp;train_x.reshape(60000,&amp;nbsp;-1)&amp;nbsp;/&amp;nbsp;255&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;0~255&amp;nbsp;-&amp;gt;&amp;nbsp;0~1&amp;nbsp;범위로&amp;nbsp;변환(minmax&amp;nbsp;스케일링&amp;nbsp;효과) &lt;br /&gt;test_x_sc&amp;nbsp;&amp;nbsp;=&amp;nbsp;test_x.reshape(10000,&amp;nbsp;-1)&amp;nbsp;/&amp;nbsp;255&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;0~255&amp;nbsp;-&amp;gt;&amp;nbsp;0~1&amp;nbsp;범위로&amp;nbsp;변환(minmax&amp;nbsp;스케일링&amp;nbsp;효과) &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;원핫&amp;nbsp;인코딩 &lt;br /&gt;import&amp;nbsp;pandas&amp;nbsp;as&amp;nbsp;pd &lt;br /&gt;train_y10&amp;nbsp;=&amp;nbsp;pd.get_dummies(train_y,&amp;nbsp;prefix='Y').astype(int).values &lt;br /&gt;test_y10&amp;nbsp;&amp;nbsp;=&amp;nbsp;pd.get_dummies(test_y,&amp;nbsp;prefix='Y').astype(int).values &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;3.&amp;nbsp;모델링 &lt;br /&gt;#&amp;nbsp;1)&amp;nbsp;seed&amp;nbsp;고정 &lt;br /&gt;import&amp;nbsp;numpy&amp;nbsp;as&amp;nbsp;np &lt;br /&gt;import&amp;nbsp;tensorflow&amp;nbsp;as&amp;nbsp;tf &lt;br /&gt;seed&amp;nbsp;=&amp;nbsp;0 &lt;br /&gt;np.random.seed(seed) &lt;br /&gt;tf.random.set_seed(seed) &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;2)&amp;nbsp;모델&amp;nbsp;정의 &lt;br /&gt;from&amp;nbsp;tensorflow.keras.layers&amp;nbsp;import&amp;nbsp;Input &lt;br /&gt;from&amp;nbsp;keras&amp;nbsp;import&amp;nbsp;Sequential &lt;br /&gt;from&amp;nbsp;keras.layers&amp;nbsp;import&amp;nbsp;Dense &lt;br /&gt;&lt;br /&gt;model&amp;nbsp;=&amp;nbsp;Sequential() &lt;br /&gt;model.add(Input(shape=(train_x_sc.shape[1],&amp;nbsp;)))&amp;nbsp;&amp;nbsp; &lt;br /&gt;model.add(Dense(392,&amp;nbsp;activation='relu')) &lt;br /&gt;model.add(Dense(196,&amp;nbsp;activation='relu')) &lt;br /&gt;model.add(Dense(98,&amp;nbsp;activation='relu')) &lt;br /&gt;model.add(Dense(10,&amp;nbsp;activation='softmax'))&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;3)&amp;nbsp;오차함수,&amp;nbsp;최적화&amp;nbsp;결정 &lt;br /&gt;model.compile(optimizer='adam',&amp;nbsp;loss='categorical_crossentropy',&amp;nbsp;metrics=['accuracy']) &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;4)&amp;nbsp;early&amp;nbsp;stopping&amp;nbsp;rule &lt;br /&gt;from&amp;nbsp;tensorflow.keras.callbacks&amp;nbsp;import&amp;nbsp;EarlyStopping &lt;br /&gt;es&amp;nbsp;=&amp;nbsp;EarlyStopping(monitor='val_loss',&amp;nbsp;patience=10,&amp;nbsp;restore_best_weights&amp;nbsp;=&amp;nbsp;True)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;5)&amp;nbsp;학습 &lt;br /&gt;#&amp;nbsp;*&amp;nbsp;각&amp;nbsp;에포크마다&amp;nbsp;반복&amp;nbsp;수&amp;nbsp;=&amp;nbsp;60000&amp;nbsp;*&amp;nbsp;0.75&amp;nbsp;/&amp;nbsp;10&amp;nbsp;=&amp;nbsp;4500 &lt;br /&gt;hist&amp;nbsp;=&amp;nbsp;model.fit(train_x_sc,&amp;nbsp;train_y10,&amp;nbsp;validation_split=0.25,&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;batch_size&amp;nbsp;=&amp;nbsp;10,&amp;nbsp;epochs=50000,&amp;nbsp;callbacks=[es])&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#&amp;nbsp;6)&amp;nbsp;평가 &lt;br /&gt;model.evaluate(train_x_sc,&amp;nbsp;train_y10)[1]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;0.9829 &lt;br /&gt;model.evaluate(test_x_sc,&amp;nbsp;test_y10)[1]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;0.9763 &lt;br /&gt;&lt;br /&gt;acc&amp;nbsp;=&amp;nbsp;hist.history['accuracy'] &lt;br /&gt;plt.plot(range(1,&amp;nbsp;len(acc)+1),&amp;nbsp;hist.history['accuracy'],&amp;nbsp;label&amp;nbsp;=&amp;nbsp;'train') &lt;br /&gt;plt.plot(range(1,&amp;nbsp;len(acc)+1),&amp;nbsp;hist.history['val_accuracy'],&amp;nbsp;label&amp;nbsp;=&amp;nbsp;'val') &lt;br /&gt;plt.legend() &lt;br /&gt;plt.xticks(range(1,&amp;nbsp;len(acc)+1)) &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#&amp;nbsp;4.&amp;nbsp;예측&amp;nbsp;실패한&amp;nbsp;데이터&amp;nbsp;시각화 &lt;br /&gt;pre_te&amp;nbsp;=&amp;nbsp;model.predict(test_x_sc).argmax(axis=1) &lt;br /&gt;&lt;br /&gt;test_fail&amp;nbsp;=&amp;nbsp;test_x[&amp;nbsp;pre_te&amp;nbsp;!=&amp;nbsp;test_y,&amp;nbsp;:,&amp;nbsp;:] &lt;br /&gt;y_true_fail&amp;nbsp;=&amp;nbsp;test_y[&amp;nbsp;pre_te&amp;nbsp;!=&amp;nbsp;test_y] &lt;br /&gt;y_pre_fail&amp;nbsp;=&amp;nbsp;pre_te[&amp;nbsp;pre_te&amp;nbsp;!=&amp;nbsp;test_y] &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;시각화 &lt;br /&gt;fig,&amp;nbsp;ax&amp;nbsp;=&amp;nbsp;plt.subplots(5,5) &lt;br /&gt;for&amp;nbsp;i&amp;nbsp;in&amp;nbsp;range(0,25)&amp;nbsp;:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;i는&amp;nbsp;y축&amp;nbsp;좌표 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;row&amp;nbsp;=&amp;nbsp;i&amp;nbsp;//&amp;nbsp;5&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;5로&amp;nbsp;나눈&amp;nbsp;몫 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;col&amp;nbsp;=&amp;nbsp;i&amp;nbsp;%&amp;nbsp;5&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;5로&amp;nbsp;나눈&amp;nbsp;나머지 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ax[row,&amp;nbsp;col].imshow(test_fail[i]) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ax[row,&amp;nbsp;col].set_title('true&amp;nbsp;:&amp;nbsp;%s,&amp;nbsp;pre&amp;nbsp;:&amp;nbsp;%s'&amp;nbsp;%&amp;nbsp;(y_true_fail[i],&amp;nbsp;y_pre_fail[i])) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ax[row,&amp;nbsp;col].axis('off')&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;919&quot; data-origin-height=&quot;900&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cuY8Mj/dJMcaazq7pz/3dWsIX4jRug6GegZvnStPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuY8Mj/dJMcaazq7pz/3dWsIX4jRug6GegZvnStPK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuY8Mj/dJMcaazq7pz/3dWsIX4jRug6GegZvnStPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcuY8Mj%2FdJMcaazq7pz%2F3dWsIX4jRug6GegZvnStPK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;919&quot; height=&quot;900&quot; data-origin-width=&quot;919&quot; data-origin-height=&quot;900&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;04 CNN 모델을 사용한 이미지 분석 &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;컨볼루션 신경망 (CNN)은 입력된 이미지에서 다시 한 번 특징을 추출하기 위해 마스크 (필터, 윈도 또는 커널)을 도입하는 기법이다. (마스크가 가중치이다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;즉, 하나의 레이어가 필터를 입혀서 기존 이미지를 요약하는 역할을 하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;마스크의 크기 및 개수는 개인이 설정하는 하이퍼파라미터로, 마스크의 크기 자체는 모델을 설계하는 사람이 선택한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;마스크의 크기가 작아질수록 처리 시간이 오래 걸린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;각 마스크가 공간의 패턴에 집중하며 돌아다니면서 가중치를 각 픽셀에 곱하고, 그러한 과정을 반복해서 새롭게 만들어진 층을 컨볼루션 (합성곱)이라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이미지 내에서도 불필요한 특징이 있을 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이미지 데이터셋 자체가 원체 크기 때문에 불필요한 특징을 제하고 의미있는 신호만 남기는 것을 풀링이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 때 풀링을 어떻게 지정하냐에 따라 달라지는데, 평균으로 요약해 도출하면 average pooling, 최소로 요약해 도출하면 min pooling, 최대로 요약해 도출하면 max pooling이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;(일반적으로 신호가 강할수록 의미가 있기 때문에 max pooling을 사용한다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 과정을 통해 불필요한 정보를 간추리게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) 데이터 로딩(RGB로 변환된 데이터) &lt;br /&gt;from&amp;nbsp;tensorflow.keras.datasets&amp;nbsp;import&amp;nbsp;mnist &lt;br /&gt;import&amp;nbsp;numpy&amp;nbsp;as&amp;nbsp;np &lt;br /&gt;&lt;br /&gt;(train_x,&amp;nbsp;train_y),&amp;nbsp;(test_x,&amp;nbsp;test_y)&amp;nbsp;=&amp;nbsp;mnist.load_data()&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;데이터&amp;nbsp;확인 &lt;br /&gt;train_x.shape&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;train_x[0].shape &lt;br /&gt;plt.imshow(train_x[0])&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) 변환&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 1) 설명변수 (스케일링, 차원 변경)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;** Conv2D 층은 한 데이터의 입력을 (높이, 너비, 채널) 형태로 요구&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(채널 : 흑백의 이미지인지 (1) 컬러의 이미지인지 (3)에 대한 차이)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;** NN 모델은 스케일링에 매우 민감 -&amp;gt; 사전 스케일링이 필수적&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;train_x_sc&amp;nbsp;=&amp;nbsp;train_x&amp;nbsp;/&amp;nbsp;255 &lt;br /&gt;test_x_sc&amp;nbsp;=&amp;nbsp;test_x&amp;nbsp;/&amp;nbsp;255 &lt;br /&gt;&lt;br /&gt;train_x_sc&amp;nbsp;=&amp;nbsp;train_x_sc.reshape((60000,&amp;nbsp;28,&amp;nbsp;28,&amp;nbsp;1)) &lt;br /&gt;test_x_sc&amp;nbsp;=&amp;nbsp;test_x_sc.reshape((10000,&amp;nbsp;28,&amp;nbsp;28,&amp;nbsp;1)) &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 2) 종속변수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;train_y10&amp;nbsp;=&amp;nbsp;pd.get_dummies(train_y).astype(int).values &lt;br /&gt;test_y10&amp;nbsp;=&amp;nbsp;pd.get_dummies(test_y).astype(int).values&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# ** 참고 : 원핫 인코딩&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 1) pandas&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pd.get_dummies(train_y).astype(int).values&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 2) keras&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;from&amp;nbsp;tensorflow.keras.utils&amp;nbsp;import&amp;nbsp;to_categorical &lt;br /&gt;to_categorical(train_y)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3) 모델링&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;1)&amp;nbsp;seed&amp;nbsp;고정 &lt;br /&gt;import&amp;nbsp;numpy&amp;nbsp;as&amp;nbsp;np &lt;br /&gt;import&amp;nbsp;tensorflow&amp;nbsp;as&amp;nbsp;tf &lt;br /&gt;seed&amp;nbsp;=&amp;nbsp;0 &lt;br /&gt;np.random.seed(seed) &lt;br /&gt;tf.random.set_seed(seed) &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;2)&amp;nbsp;모델&amp;nbsp;정의 &lt;br /&gt;from&amp;nbsp;tensorflow.keras.layers&amp;nbsp;import&amp;nbsp;Input &lt;br /&gt;from&amp;nbsp;keras&amp;nbsp;import&amp;nbsp;Sequential &lt;br /&gt;from&amp;nbsp;keras.layers&amp;nbsp;import&amp;nbsp;Dense,&amp;nbsp;Conv2D,&amp;nbsp;Dropout,&amp;nbsp;Flatten,&amp;nbsp;MaxPooling2D,&amp;nbsp;Input &lt;br /&gt;&lt;br /&gt;model&amp;nbsp;=&amp;nbsp;Sequential() &lt;br /&gt;model.add(Input(shape=(28,&amp;nbsp;28,&amp;nbsp;1)))&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;한&amp;nbsp;데이터&amp;nbsp;기준&amp;nbsp;사이즈&amp;nbsp;전달 &lt;br /&gt;model.add(Conv2D(32,&amp;nbsp;kernel_size&amp;nbsp;=&amp;nbsp;(2,2),&amp;nbsp;activation='relu'))&amp;nbsp;#&amp;nbsp;2*2&amp;nbsp;크기의&amp;nbsp;마스크를&amp;nbsp;32개&amp;nbsp;씌움 &lt;br /&gt;model.add(Conv2D(32,&amp;nbsp;kernel_size&amp;nbsp;=&amp;nbsp;(2,2),&amp;nbsp;activation='relu')) &lt;br /&gt;model.add(MaxPooling2D(2,2)) &lt;br /&gt;model.add(Flatten()) &lt;br /&gt;model.add(Dense(128,&amp;nbsp;activation='relu')) &lt;br /&gt;model.add(Dropout(0.5)) &lt;br /&gt;model.add(Dense(10,&amp;nbsp;activation='softmax')) &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;3)&amp;nbsp;compile &lt;br /&gt;model.compile('adam',&amp;nbsp;loss='categorical_crossentropy',&amp;nbsp;metrics=['accuracy']) &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;4)&amp;nbsp;early&amp;nbsp;stopping&amp;nbsp;rule &lt;br /&gt;from&amp;nbsp;tensorflow.keras.callbacks&amp;nbsp;import&amp;nbsp;EarlyStopping &lt;br /&gt;es&amp;nbsp;=&amp;nbsp;EarlyStopping(monitor='val_loss',&amp;nbsp;patience=10,&amp;nbsp;restore_best_weights&amp;nbsp;=&amp;nbsp;True)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;#&amp;nbsp;5)&amp;nbsp;학습 &lt;br /&gt;#&amp;nbsp;*&amp;nbsp;각&amp;nbsp;에포크마다&amp;nbsp;반복&amp;nbsp;수&amp;nbsp;=&amp;nbsp;60000&amp;nbsp;*&amp;nbsp;0.75&amp;nbsp;/&amp;nbsp;10&amp;nbsp;=&amp;nbsp;4500 &lt;br /&gt;hist&amp;nbsp;=&amp;nbsp;model.fit(train_x_sc,&amp;nbsp;train_y10,&amp;nbsp;validation_split=0.25,&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;batch_size&amp;nbsp;=&amp;nbsp;10,&amp;nbsp;epochs=50000,&amp;nbsp;callbacks=[es])&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;6)&amp;nbsp;평가 &lt;br /&gt;model.evaluate(train_x_sc,&amp;nbsp;train_y10)[1]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;0.9939 &lt;br /&gt;model.evaluate(test_x_sc,&amp;nbsp;test_y10)[1]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;0.9869 &lt;/p&gt;
&lt;pre id=&quot;code_1785136329127&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;model.evaluate(train_x_sc, train_y10)[1]     # 0.9829
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 4s 2ms/step - accuracy: 0.9939 - loss: 0.0221        
Out[269]: 0.9938666820526123

model.evaluate(test_x_sc, test_y10)[1]       # 0.9763
313/313 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - accuracy: 0.9869 - loss: 0.0499     
Out[270]: 0.9868999719619751&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;acc&amp;nbsp;=&amp;nbsp;hist.history['accuracy'] &lt;br /&gt;plt.plot(range(1,&amp;nbsp;len(acc)+1),&amp;nbsp;hist.history['accuracy'],&amp;nbsp;label&amp;nbsp;=&amp;nbsp;'train') &lt;br /&gt;plt.plot(range(1,&amp;nbsp;len(acc)+1),&amp;nbsp;hist.history['val_accuracy'],&amp;nbsp;label&amp;nbsp;=&amp;nbsp;'val') &lt;br /&gt;plt.legend() &lt;br /&gt;plt.xticks(range(1,&amp;nbsp;len(acc)+1)) &lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;567&quot; data-origin-height=&quot;416&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmFshe/dJMcajwmLYx/4QNYWnKglp9BziJRfJSi6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmFshe/dJMcajwmLYx/4QNYWnKglp9BziJRfJSi6K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmFshe/dJMcajwmLYx/4QNYWnKglp9BziJRfJSi6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmFshe%2FdJMcajwmLYx%2F4QNYWnKglp9BziJRfJSi6K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;567&quot; height=&quot;416&quot; data-origin-width=&quot;567&quot; data-origin-height=&quot;416&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4) 예측 실패한 데이터 시각화 &lt;br /&gt;pre_te&amp;nbsp;=&amp;nbsp;model.predict(test_x_sc).argmax(axis=1) &lt;br /&gt;&lt;br /&gt;test_fail&amp;nbsp;=&amp;nbsp;test_x[&amp;nbsp;pre_te&amp;nbsp;!=&amp;nbsp;test_y,&amp;nbsp;:,&amp;nbsp;:] &lt;br /&gt;y_true_fail&amp;nbsp;=&amp;nbsp;test_y[&amp;nbsp;pre_te&amp;nbsp;!=&amp;nbsp;test_y] &lt;br /&gt;y_pre_fail&amp;nbsp;=&amp;nbsp;pre_te[&amp;nbsp;pre_te&amp;nbsp;!=&amp;nbsp;test_y] &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;시각화 &lt;br /&gt;fig,&amp;nbsp;ax&amp;nbsp;=&amp;nbsp;plt.subplots(5,5) &lt;br /&gt;for&amp;nbsp;i&amp;nbsp;in&amp;nbsp;range(0,25)&amp;nbsp;:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;i는&amp;nbsp;y축&amp;nbsp;좌표 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;row&amp;nbsp;=&amp;nbsp;i&amp;nbsp;//&amp;nbsp;5&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;5로&amp;nbsp;나눈&amp;nbsp;몫 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;col&amp;nbsp;=&amp;nbsp;i&amp;nbsp;%&amp;nbsp;5&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;5로&amp;nbsp;나눈&amp;nbsp;나머지 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ax[row,&amp;nbsp;col].imshow(test_fail[i]) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ax[row,&amp;nbsp;col].set_title('true&amp;nbsp;:&amp;nbsp;%s,&amp;nbsp;pre&amp;nbsp;:&amp;nbsp;%s'&amp;nbsp;%&amp;nbsp;(y_true_fail[i],&amp;nbsp;y_pre_fail[i])) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ax[row,&amp;nbsp;col].axis('off')&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1442&quot; data-origin-height=&quot;919&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KPjre/dJMcabE16Qs/1gzJOSJs121m08AfGcPtFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KPjre/dJMcabE16Qs/1gzJOSJs121m08AfGcPtFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KPjre/dJMcabE16Qs/1gzJOSJs121m08AfGcPtFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKPjre%2FdJMcabE16Qs%2F1gzJOSJs121m08AfGcPtFk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1442&quot; height=&quot;919&quot; data-origin-width=&quot;1442&quot; data-origin-height=&quot;919&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>아이티윌_데이터 분석 55기/강의내용_딥러닝</category>
      <author>ecosso</author>
      <guid isPermaLink="true">https://ecosso.tistory.com/144</guid>
      <comments>https://ecosso.tistory.com/144#entry144comment</comments>
      <pubDate>Mon, 27 Jul 2026 16:22:48 +0900</pubDate>
    </item>
    <item>
      <title>#2 2일차_인공신경망의 구조, 실습</title>
      <link>https://ecosso.tistory.com/143</link>
      <description>&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;01 인공신경망의 구조&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-01 layer + node&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-02 활성함수&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-03 오차함수&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-04 최적화&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-05 오차 역전파&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-06 기울기 소실&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;02 실습&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-01 ANN - 회귀&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-02 ANN 튜닝 - batch_size, early stopping (적절한 epoch 결정)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-03 ANN - 분류&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;01 인공신경망의 구조&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-01 layer + node&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;입력층 - 중간층 - 출력층 구조로 구성되어 있으며 각 층마다 여러 노드(뉴런)이 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt; 입력층 : 데이터를 전달받는 층 (노드의 수 = 피쳐 수)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt; 출력층 : 최종 예측 결과를 리턴하는 층 (노드의 수 = 출력 결과)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 회귀분석 : 1개의 target&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 분류분석 :&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;ex) 사망여부 -&amp;gt; 1개 target -&amp;gt; 원핫인코딩 (클래스별로 서로 다른 target 생성)* =&amp;gt; Y_사망 Y_생존 (출력층 노드 수 2개)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;ex) iris data 품종 -&amp;gt; 1개 target -&amp;gt; 원핫인코딩 =&amp;gt; Y_1 Y_2 Y_3 (출력층 노드 수 3개)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt; 중간층 : 하나의 층 구성에서부터 층을 추가하여 성능 비교 (층의 수, 노드 수 -&amp;gt; 사용자 결정*)&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-02 활성함수 (사용자 결정*)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신호의 가중합을 다음 층에 전달할지의 여부를 결정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sigmoid (0 : x &amp;lt; 0, 1 : x &amp;gt;=0), 렐루, 스텝 등&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가중합의 크기가 0보다 작으면 0, 가중합의 크기가 0보다 크거나 같으면 1이다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-03 오차함수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오차를 계산하여 오차를 줄이는 방향으로 기울기의 최적 구간을 탐색한다. (경사하강법)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 회귀 분석 : MSE, RMSE, MAE 등&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 분류 분석 : cross entropy, binary cross entropy 등 (사용자 결정*)&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-04 최적화 (사용자 결정*)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 데이터 수 = N&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;batch_size : 한 번 가중치 업데이트 시 사용하는 데이터의 수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;iteration(step) : 1 epoch 동안 발생하는 업데이트 수 = N / batch_size&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;epochs : 전체 데이터를 1회 학습하는 단위&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex) N = 100&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; batch_size = 1&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 epoch마다 100건 중 단 1개의 데이터로 가중치 업데이트 100번 반복&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;batch_size = 100 (full batch = 배치 경사하강법)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 epoch마다 100건 모두 사용하여 가중치 업데이트 1번 반복&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;batch_size = 10 (mini batch)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 epoch마다 100/10 = 10개의 데이터로 가중치 업데이트 10번 반복&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-05 오차 역전파&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기울기와 오차와의 관계를 통해 기울기의 최적화를 진행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중간층에서는 오차와 기울기의 관계를 파악할 수 없는 문제가 발생하며, 순전파를 통한 출력층의 오차를 이전층으로 분해하여 전달, 중간층에서도 기울기와 오차의 관계를 파악할 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-06 기울기 소실&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;층이 늘어나면서 기울기가 0이 되는 현상이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시그모이드의 편미분값이 1보다 작아서 층마다 미분값이 곱해지는 과정에서 기울기가 0에 가까워진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하이퍼볼릭 탄젠트, 렐루, 소프트플러스 등으로 해결한다. (주로 렐루 사용)&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;02 실습&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-01 ANN - 회귀&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) 데이터 로딩&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;import&amp;nbsp;pandas&amp;nbsp;as&amp;nbsp;pd &lt;br /&gt;boston&amp;nbsp;=&amp;nbsp;pd.read_csv('boston.csv')&lt;/p&gt;
&lt;pre id=&quot;code_1784859004339&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;boston.head()
Out[14]: 
      crim    zn  indus  chas    nox  ...  tax  ptratio   black  lstat  medv
0  0.00632  18.0   2.31     0  0.538  ...  296     15.3  396.90   4.98  24.0
1  0.02731   0.0   7.07     0  0.469  ...  242     17.8  396.90   9.14  21.6
2  0.02729   0.0   7.07     0  0.469  ...  242     17.8  392.83   4.03  34.7
3  0.03237   0.0   2.18     0  0.458  ...  222     18.7  394.63   2.94  33.4
4  0.06905   0.0   2.18     0  0.458  ...  222     18.7  396.90   5.33  36.2

[5 rows x 14 columns]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;y&amp;nbsp;=&amp;nbsp;boston['medv'] &lt;br /&gt;X&amp;nbsp;=&amp;nbsp;boston.drop(columns='medv')&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) 스케일링&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;from&amp;nbsp;sklearn.preprocessing&amp;nbsp;import&amp;nbsp;StandardScaler,&amp;nbsp;MinMaxScaler &lt;br /&gt;m_sc&amp;nbsp;=&amp;nbsp;StandardScaler() &lt;br /&gt;X_sc&amp;nbsp;=&amp;nbsp;m_sc.fit_transform(X)&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3) train / test 분리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;from&amp;nbsp;sklearn.model_selection&amp;nbsp;import&amp;nbsp;train_test_split &lt;br /&gt;train_x,&amp;nbsp;test_x,&amp;nbsp;train_y,&amp;nbsp;test_y&amp;nbsp;=&amp;nbsp;train_test_split(X_sc,&amp;nbsp;y,&amp;nbsp;random_state=0)&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4) 모델링&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;(1) seed 고정 (딥러닝은 외부에서 seed를 고정)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;import&amp;nbsp;numpy&amp;nbsp;as&amp;nbsp;np &lt;br /&gt;import&amp;nbsp;tensorflow&amp;nbsp;as&amp;nbsp;tf &lt;br /&gt;seed&amp;nbsp;=&amp;nbsp;0 &lt;br /&gt;np.random.seed(seed) &lt;br /&gt;tf.random.set_seed(seed)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;(2) 모델 정의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;from&amp;nbsp;tensorflow.keras.layers&amp;nbsp;import&amp;nbsp;Input &lt;br /&gt;from&amp;nbsp;keras&amp;nbsp;import&amp;nbsp;Sequential &lt;br /&gt;from&amp;nbsp;keras.layers&amp;nbsp;import&amp;nbsp;Dense &lt;br /&gt;&lt;br /&gt;model&amp;nbsp;=&amp;nbsp;Sequential() &lt;br /&gt;model.add(Input(shape&amp;nbsp;=&amp;nbsp;(train_x.shape[1],&amp;nbsp;)))&amp;nbsp;&amp;nbsp;#&amp;nbsp;입력층은&amp;nbsp;Input&amp;nbsp;함수로&amp;nbsp;노드&amp;nbsp;구성&amp;nbsp;(노드수&amp;nbsp;=&amp;nbsp;설명변수&amp;nbsp;수) &lt;br /&gt;model.add(Dense(6,&amp;nbsp;activation&amp;nbsp;=&amp;nbsp;'sigmoid')) &lt;br /&gt;model.add(Dense(1))&amp;nbsp;&amp;nbsp;#&amp;nbsp;회귀분석&amp;nbsp;출력층은&amp;nbsp;노드는&amp;nbsp;1개로&amp;nbsp;구성,&amp;nbsp;활성함수&amp;nbsp;None&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 중간층에만 activation function을 넣어준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;(3) 오차함수, 최적화 결정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;model.compile(optimizer='adam',&amp;nbsp;loss='mse',&amp;nbsp;metrics=['mae'])&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;(4) 학습&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;model.fit(train_x,&amp;nbsp;train_y,&amp;nbsp;epochs=500,&amp;nbsp;batch_size&amp;nbsp;=&amp;nbsp;10)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확률적 경사 하강법을 사용하며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;batch_size = 10 : 전체 데이터의 10%를 랜덤 선택하여 가중치 업데이트에 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;배치사이즈가 작아지면 속도는 빨라지나 안정성은 낮아진다. (편차가 커진다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;epochs = 500 : 위 스텝을 500번 반복한다.&lt;/p&gt;
&lt;pre id=&quot;code_1784861607140&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...
Epoch 498/500
38/38 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - loss: 10.2966 - mae: 2.3441
Epoch 499/500
38/38 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - loss: 10.2831 - mae: 2.3428
Epoch 500/500
38/38 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - loss: 10.2696 - mae: 2.3414

Out[42]: &amp;lt;keras.src.callbacks.history.History at 0x20fe69ac6e0&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;379건의 10%를 사용하였기 때문에 Epoch 하단에 38이라는 숫자가 기재된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;train_x.shape&lt;/p&gt;
&lt;pre id=&quot;code_1784861688924&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;train_x.shape
Out[43]: (379, 13)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;(5) 평가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;model.evaluate(test_x, test_y)[0]&amp;nbsp; &amp;nbsp; &amp;nbsp;# loss (MSE)&lt;/p&gt;
&lt;pre id=&quot;code_1784862004165&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;model.evaluate(test_x, test_y)[0]
4/4 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - loss: 21.1461 - mae: 3.0215 
Out[45]: 21.14611053466797&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;model.evaluate(test_x, test_y)[1]&amp;nbsp; &amp;nbsp; # metrics (MAE)&lt;/p&gt;
&lt;pre id=&quot;code_1784862030501&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;model.evaluate(test_x, test_y)[1]
4/4 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - loss: 21.1461 - mae: 3.0215 
Out[46]: 3.0215461254119873&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; 500번 epoch train loss : 10.2696, test loss : 21.1461 (과적합 가능성 충분)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;(6) 예측&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;model.predict(test_x) &lt;br /&gt;test_y&lt;/p&gt;
&lt;pre id=&quot;code_1784863015325&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;test_y
Out[52]: 
329    22.6
371    50.0
219    23.0
403     8.3
78     21.2

49     19.4
498    21.2
309    20.3
124    18.8
306    33.4
Name: medv, Length: 127, dtype: float64&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;model.add(Dense(52,&amp;nbsp;activation&amp;nbsp;=&amp;nbsp;'relu')) &lt;br /&gt;model.add(Dense(26,&amp;nbsp;activation&amp;nbsp;=&amp;nbsp;'relu')) &lt;br /&gt;model.add(Dense(13,&amp;nbsp;activation&amp;nbsp;=&amp;nbsp;'relu'))&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 다층으로 구성된 경우 sigmoid가 적절하지 않다. 이러한 경우일 때는 relu를 사용하는 것이 좋다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-02 ANN 튜닝 - batch_size, early stopping (적절한 epoch 결정)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1) 데이터 로딩&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;import&amp;nbsp;pandas&amp;nbsp;as&amp;nbsp;pd&lt;br /&gt;boston&amp;nbsp;=&amp;nbsp;pd.read_csv('boston.csv')&lt;/p&gt;
&lt;pre id=&quot;code_1784864930707&quot; class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;boston.head()
Out[14]: 
      crim    zn  indus  chas    nox  ...  tax  ptratio   black  lstat  medv
0  0.00632  18.0   2.31     0  0.538  ...  296     15.3  396.90   4.98  24.0
1  0.02731   0.0   7.07     0  0.469  ...  242     17.8  396.90   9.14  21.6
2  0.02729   0.0   7.07     0  0.469  ...  242     17.8  392.83   4.03  34.7
3  0.03237   0.0   2.18     0  0.458  ...  222     18.7  394.63   2.94  33.4
4  0.06905   0.0   2.18     0  0.458  ...  222     18.7  396.90   5.33  36.2

[5 rows x 14 columns]&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;y&amp;nbsp;=&amp;nbsp;boston['medv']&lt;br /&gt;X&amp;nbsp;=&amp;nbsp;boston.drop(columns='medv')&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style1&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2) 스케일링&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;from&amp;nbsp;sklearn.preprocessing&amp;nbsp;import&amp;nbsp;StandardScaler,&amp;nbsp;MinMaxScaler&lt;br /&gt;m_sc&amp;nbsp;=&amp;nbsp;StandardScaler()&lt;br /&gt;X_sc&amp;nbsp;=&amp;nbsp;m_sc.fit_transform(X)&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style1&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;3) train / test 분리&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;from&amp;nbsp;sklearn.model_selection&amp;nbsp;import&amp;nbsp;train_test_split&lt;br /&gt;train_x,&amp;nbsp;test_x,&amp;nbsp;train_y,&amp;nbsp;test_y&amp;nbsp;=&amp;nbsp;train_test_split(X_sc,&amp;nbsp;y,&amp;nbsp;random_state=0)&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style1&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;4) 모델링&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;(1) seed 고정 (딥러닝은 외부에서 seed를 고정)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;import&amp;nbsp;numpy&amp;nbsp;as&amp;nbsp;np&lt;br /&gt;import&amp;nbsp;tensorflow&amp;nbsp;as&amp;nbsp;tf&lt;br /&gt;seed&amp;nbsp;=&amp;nbsp;0&lt;br /&gt;np.random.seed(seed)&lt;br /&gt;tf.random.set_seed(seed)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;(2) 모델 정의&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;from&amp;nbsp;tensorflow.keras.layers&amp;nbsp;import&amp;nbsp;Input&lt;br /&gt;from&amp;nbsp;keras&amp;nbsp;import&amp;nbsp;Sequential&lt;br /&gt;from&amp;nbsp;keras.layers&amp;nbsp;import&amp;nbsp;Dense&lt;br /&gt;&lt;br /&gt;model&amp;nbsp;=&amp;nbsp;Sequential()&lt;br /&gt;model.add(Input(shape&amp;nbsp;=&amp;nbsp;(train_x.shape[1],&amp;nbsp;)))&amp;nbsp;&amp;nbsp;#&amp;nbsp;입력층은&amp;nbsp;Input&amp;nbsp;함수로&amp;nbsp;노드&amp;nbsp;구성&amp;nbsp;(노드수&amp;nbsp;=&amp;nbsp;설명변수&amp;nbsp;수)&lt;br /&gt;model.add(Dense(6,&amp;nbsp;activation&amp;nbsp;=&amp;nbsp;'sigmoid'))&lt;br /&gt;model.add(Dense(1))&amp;nbsp;&amp;nbsp;#&amp;nbsp;회귀분석&amp;nbsp;출력층은&amp;nbsp;노드는&amp;nbsp;1개로&amp;nbsp;구성,&amp;nbsp;활성함수&amp;nbsp;None&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;* 중간층에만 activation function을 넣어준다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;(3) 오차함수, 최적화 결정&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;model.compile(optimizer='adam',&amp;nbsp;loss='mse',&amp;nbsp;metrics=['mae'])&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;span style=&quot;background-color: #f3c000;&quot;&gt;(4) early stopping rule&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;충분한 epoch로 학습, 매 스텝마다 검증 오차를 체크하여 더 이상 개선이 없을 경우 학습을 중단한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;ex= EarlyStopping(monitor='val_loss', # 모니터링 기준&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; patience=10,&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 10회 동안 개선이 없을 경우 중단&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; restore_best_weights = True)&amp;nbsp; # 최적 오차 도출&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;# 멈춘 시점이 아닌 검증오차가 최소였던 지점의 오차 기준으로 최종 모델을 선정&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;(5) 학습&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;hist = model.fit(train_x, train_y,&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;validation_split = 0.25,&amp;nbsp; # train 데이터의 25%를 검증용 데이터로 선택&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;batch_size = 10, # 전체 데이터의 10%f를 랜덤 선택하여 가중치 업데이트에 사용&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;epochs = 50000, # 충분히 크게 설정&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;callbacks = [es]) # early stopping rule 추가&lt;/p&gt;
&lt;pre id=&quot;code_1784865629870&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...
Epoch 126/50000
29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 3ms/step - loss: 2.3838 - mae: 1.1503 - val_loss: 6.4815 - val_mae: 1.9125
Epoch 127/50000
29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 3ms/step - loss: 2.3591 - mae: 1.1438 - val_loss: 6.3612 - val_mae: 1.9056
Epoch 128/50000
29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 3ms/step - loss: 2.3255 - mae: 1.1320 - val_loss: 6.4057 - val_mae: 1.9069
Epoch 129/50000
29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 3ms/step - loss: 2.3205 - mae: 1.1334 - val_loss: 6.3793 - val_mae: 1.9044
Epoch 130/50000
29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 3ms/step - loss: 2.3036 - mae: 1.1307 - val_loss: 6.4116 - val_mae: 1.9115&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;(6) 평가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hist.history['loss']&amp;nbsp;#&amp;nbsp;매&amp;nbsp;epoch&amp;nbsp;마다의&amp;nbsp;학습오차 &lt;br /&gt;hist.history['val_loss']&amp;nbsp;#&amp;nbsp;매&amp;nbsp;epoch&amp;nbsp;마다의&amp;nbsp;검증오차&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;import&amp;nbsp;matplotlib.pyplot&amp;nbsp;as&amp;nbsp;plt &lt;br /&gt;plt.plot(hist.history['loss'],&amp;nbsp;label&amp;nbsp;=&amp;nbsp;'train') &lt;br /&gt;plt.plot(hist.history['val_loss'],&amp;nbsp;label&amp;nbsp;=&amp;nbsp;'val') &lt;br /&gt;plt.legend()&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;601&quot; data-origin-height=&quot;441&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cBbRFp/dJMcaaMVcWD/GarCsZBUrdPHURJ4GDleH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cBbRFp/dJMcaaMVcWD/GarCsZBUrdPHURJ4GDleH1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cBbRFp/dJMcaaMVcWD/GarCsZBUrdPHURJ4GDleH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcBbRFp%2FdJMcaaMVcWD%2FGarCsZBUrdPHURJ4GDleH1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;601&quot; height=&quot;441&quot; data-origin-width=&quot;601&quot; data-origin-height=&quot;441&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;model.evaluate(train_x,&amp;nbsp;train_y)&lt;/p&gt;
&lt;pre id=&quot;code_1784865717990&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;model.evaluate(train_x, train_y)
12/12 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 3.5405 - mae: 1.4057 
Out[90]: [3.540541172027588, 1.405723214149475]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;model.evaluate(test_x,&amp;nbsp;test_y)&lt;/p&gt;
&lt;pre id=&quot;code_1784865725654&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;model.evaluate(test_x, test_y)
4/4 ━━━━━━━━━━━━━━━━━━━━ 0s 7ms/step - loss: 16.4689 - mae: 2.9572 
Out[91]: [16.468944549560547, 2.957167625427246]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;##&amp;nbsp;**&amp;nbsp;참고&amp;nbsp;:&amp;nbsp;위&amp;nbsp;그래프&amp;nbsp;출력&amp;nbsp;시&amp;nbsp;폰트&amp;nbsp;에러가&amp;nbsp;발생하는&amp;nbsp;경우&amp;nbsp;-&amp;gt;&amp;nbsp;기본&amp;nbsp;폰트&amp;nbsp;깨짐&amp;nbsp;현상&amp;nbsp;-&amp;gt;&amp;nbsp;폰트&amp;nbsp;캐시&amp;nbsp;재생성 &lt;br /&gt;import&amp;nbsp;matplotlib &lt;br /&gt;import&amp;nbsp;shutil,&amp;nbsp;os &lt;br /&gt;cache_dir&amp;nbsp;=&amp;nbsp;matplotlib.get_cachedir() &lt;br /&gt;print(ache_dir) &lt;br /&gt;shutil.rmtree(cache_dir)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;(7) batch size 튜닝&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bs&amp;nbsp;=&amp;nbsp;[10,&amp;nbsp;20,&amp;nbsp;50,&amp;nbsp;train_x.shape[0]] &lt;br /&gt;for&amp;nbsp;i&amp;nbsp;in&amp;nbsp;bs&amp;nbsp;: &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;seed&amp;nbsp;=&amp;nbsp;0 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;np.random.seed(seed) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;tf.random.set_seed(seed) &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;모델&amp;nbsp;정의 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;model&amp;nbsp;=&amp;nbsp;Sequential() &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;model.add(Input(shape=(train_x.shape[1],&amp;nbsp;)))&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;model.add(Dense(6,&amp;nbsp;activation='relu')) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;model.add(Dense(1))&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;오차함수,&amp;nbsp;최적화&amp;nbsp;결정 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;model.compile(optimizer='adam',&amp;nbsp;loss='mse',&amp;nbsp;metrics=['mae','r2_score']) &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;early&amp;nbsp;stopping&amp;nbsp;rule &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;es&amp;nbsp;=&amp;nbsp;EarlyStopping(monitor='val_loss',&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;patience=10,&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;restore_best_weights&amp;nbsp;=&amp;nbsp;True)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;학습 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;hist&amp;nbsp;=&amp;nbsp;model.fit(train_x,&amp;nbsp;train_y,&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;validation_split=0.25,&amp;nbsp;verbose=&amp;nbsp;0,&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;batch_size&amp;nbsp;=&amp;nbsp;i,&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;epochs=50000,&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;callbacks=[es])&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;최적&amp;nbsp;반복수&amp;nbsp;확인 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;be&amp;nbsp;=&amp;nbsp;np.argmin(hist.history['val_loss'])&amp;nbsp;+&amp;nbsp;1 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f'batch_size&amp;nbsp;:&amp;nbsp;{i},&amp;nbsp;최적&amp;nbsp;반복수&amp;nbsp;:&amp;nbsp;{be}') &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;평가 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(model.evaluate(train_x,&amp;nbsp;train_y)[0]) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(model.evaluate(test_x,&amp;nbsp;test_y)[0])&lt;/p&gt;
&lt;pre id=&quot;code_1784874858441&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;batch_size : 10, 최적 반복수 : 1673
12/12 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 8.2063 - mae: 2.0459 - r2_score: 0.9038 
8.206317901611328
4/4 ━━━━━━━━━━━━━━━━━━━━ 0s 6ms/step - loss: 21.7978 - mae: 3.0250 - r2_score: 0.7332 
21.797828674316406
batch_size : 20, 최적 반복수 : 974
12/12 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 8.5905 - mae: 2.1381 - r2_score: 0.8993 
8.590462684631348
4/4 ━━━━━━━━━━━━━━━━━━━━ 0s 6ms/step - loss: 20.6176 - mae: 3.0851 - r2_score: 0.7476 
20.617616653442383
batch_size : 50, 최적 반복수 : 2302
12/12 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 8.3724 - mae: 2.1259 - r2_score: 0.9019 
8.372426986694336
4/4 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - loss: 20.5260 - mae: 3.0028 - r2_score: 0.7488 
20.525999069213867
batch_size : 379, 최적 반복수 : 5407
12/12 ━━━━━━━━━━━━━━━━━━━━ 0s 3ms/step - loss: 13.3568 - mae: 2.6968 - r2_score: 0.8434
13.356831550598145
4/4 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - loss: 25.5050 - mae: 3.6179 - r2_score: 0.6878 
25.504959106445312&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-03 ANN - 분류&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회귀분석과는 다르게 출력층에서 반드시 가중합을 학습된 Y의 형태 (0 or 1)로 변환하는 장치 필요 --&amp;gt; 활성함수 (sigmoid)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력층의 유닛(노드)의 수가 1개가 아닐 수 있음*&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;(1) 데이터 로딩 &lt;br /&gt;cancer&amp;nbsp;=&amp;nbsp;pd.read_csv('cancer.csv') &lt;br /&gt;y&amp;nbsp;=&amp;nbsp;cancer['diagnosis'] &lt;br /&gt;X&amp;nbsp;=&amp;nbsp;cancer.drop(columns=['id','diagnosis']) &lt;br /&gt;&lt;br /&gt;&amp;nbsp;(2) 라벨링 &lt;br /&gt;y&amp;nbsp;=&amp;nbsp;(y&amp;nbsp;==&amp;nbsp;'Malignant').astype('int')&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;출력층&amp;nbsp;노드&amp;nbsp;수&amp;nbsp;1개로&amp;nbsp;설정&amp;nbsp;필요 &lt;br /&gt;y2&amp;nbsp;=&amp;nbsp;pd.get_dummies(y,&amp;nbsp;prefix='Y').astype('int')&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;출력층&amp;nbsp;노드&amp;nbsp;수&amp;nbsp;2개로&amp;nbsp;설정&amp;nbsp;필요 &lt;br /&gt;&lt;br /&gt;&amp;nbsp;(3) 스케일링 &lt;br /&gt;from&amp;nbsp;sklearn.preprocessing&amp;nbsp;import&amp;nbsp;MinMaxScaler &lt;br /&gt;m_sc&amp;nbsp;=&amp;nbsp;MinMaxScaler() &lt;br /&gt;X_sc&amp;nbsp;=&amp;nbsp;m_sc.fit_transform(X) &lt;br /&gt;&lt;br /&gt;&amp;nbsp;(4)train / test 분리 &lt;br /&gt;from&amp;nbsp;sklearn.model_selection&amp;nbsp;import&amp;nbsp;train_test_split &lt;br /&gt;train_x,&amp;nbsp;test_x,&amp;nbsp;train_y,&amp;nbsp;test_y&amp;nbsp;=&amp;nbsp;train_test_split(X_sc,&amp;nbsp;y2,&amp;nbsp;random_state=0) &lt;br /&gt;&lt;br /&gt;&amp;nbsp;(5) 모델링 &lt;br /&gt;#&amp;nbsp;1)&amp;nbsp;seed&amp;nbsp;고정 &lt;br /&gt;import&amp;nbsp;numpy&amp;nbsp;as&amp;nbsp;np &lt;br /&gt;import&amp;nbsp;tensorflow&amp;nbsp;as&amp;nbsp;tf &lt;br /&gt;seed&amp;nbsp;=&amp;nbsp;0 &lt;br /&gt;np.random.seed(seed) &lt;br /&gt;tf.random.set_seed(seed) &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;2)&amp;nbsp;모델&amp;nbsp;정의 &lt;br /&gt;from&amp;nbsp;tensorflow.keras.layers&amp;nbsp;import&amp;nbsp;Input &lt;br /&gt;from&amp;nbsp;keras&amp;nbsp;import&amp;nbsp;Sequential &lt;br /&gt;from&amp;nbsp;keras.layers&amp;nbsp;import&amp;nbsp;Dense &lt;br /&gt;&lt;br /&gt;model&amp;nbsp;=&amp;nbsp;Sequential() &lt;br /&gt;model.add(Input(shape=(train_x.shape[1],&amp;nbsp;)))&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;model.add(Dense(15,&amp;nbsp;activation='relu')) &lt;br /&gt;model.add(Dense(2,&amp;nbsp;activation='sigmoid'))&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;3)&amp;nbsp;오차함수,&amp;nbsp;최적화&amp;nbsp;결정 &lt;br /&gt;model.compile(optimizer='adam',&amp;nbsp;loss='binary_crossentropy',&amp;nbsp;metrics=['accuracy']) &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;4)&amp;nbsp;early&amp;nbsp;stopping&amp;nbsp;rule &lt;br /&gt;from&amp;nbsp;tensorflow.keras.callbacks&amp;nbsp;import&amp;nbsp;EarlyStopping &lt;br /&gt;es&amp;nbsp;=&amp;nbsp;EarlyStopping(monitor='val_loss',&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;patience=10,&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;restore_best_weights&amp;nbsp;=&amp;nbsp;True)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;# 5) 학습 &lt;br /&gt;hist&amp;nbsp;=&amp;nbsp;model.fit(train_x,&amp;nbsp;train_y,&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;validation_split=0.25,&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;batch_size&amp;nbsp;=&amp;nbsp;10,&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;epochs=50000,&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;callbacks=[es])&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&lt;br /&gt;# 6) 평가 &lt;br /&gt;hist.history['loss']&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;매&amp;nbsp;epoch&amp;nbsp;마다의&amp;nbsp;학습오차 &lt;br /&gt;hist.history['val_loss']&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;매&amp;nbsp;epoch&amp;nbsp;마다의&amp;nbsp;검증오차 &lt;br /&gt;hist.history['accuracy']&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;매&amp;nbsp;epoch&amp;nbsp;마다의&amp;nbsp;학습&amp;nbsp;정확도 &lt;br /&gt;hist.history['val_accuracy']&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;매&amp;nbsp;epoch&amp;nbsp;마다의&amp;nbsp;검증&amp;nbsp;정확도 &lt;br /&gt;&lt;br /&gt;import&amp;nbsp;matplotlib.pyplot&amp;nbsp;as&amp;nbsp;plt &lt;br /&gt;fig,&amp;nbsp;ax&amp;nbsp;=&amp;nbsp;plt.subplots(1,2) &lt;br /&gt;ax[0].plot(hist.history['loss'],&amp;nbsp;label&amp;nbsp;=&amp;nbsp;'train') &lt;br /&gt;ax[0].plot(hist.history['val_loss'],&amp;nbsp;label&amp;nbsp;=&amp;nbsp;'val') &lt;br /&gt;ax[0].legend() &lt;br /&gt;&lt;br /&gt;ax[1].plot(hist.history['accuracy'],&amp;nbsp;label&amp;nbsp;=&amp;nbsp;'train') &lt;br /&gt;ax[1].plot(hist.history['val_accuracy'],&amp;nbsp;label&amp;nbsp;=&amp;nbsp;'val') &lt;br /&gt;ax[1].legend() &lt;br /&gt;&lt;br /&gt;model.evaluate(train_x,&amp;nbsp;train_y)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;accuracy:&amp;nbsp;0.9883&amp;nbsp;-&amp;nbsp;loss:&amp;nbsp;0.0310&amp;nbsp; &lt;br /&gt;model.evaluate(test_x,&amp;nbsp;test_y)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;accuracy:&amp;nbsp;0.9441&amp;nbsp;-&amp;nbsp;loss:&amp;nbsp;0.2931&amp;nbsp;&lt;/p&gt;</description>
      <category>아이티윌_데이터 분석 55기/강의내용_딥러닝</category>
      <author>ecosso</author>
      <guid isPermaLink="true">https://ecosso.tistory.com/143</guid>
      <comments>https://ecosso.tistory.com/143#entry143comment</comments>
      <pubDate>Fri, 24 Jul 2026 16:18:06 +0900</pubDate>
    </item>
    <item>
      <title>#1 1일차_tensorflow, keras 설치하기</title>
      <link>https://ecosso.tistory.com/142</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;01 설치하기&lt;/b&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;01 설치하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pip&amp;nbsp;install&amp;nbsp;tensorflow&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1144&quot; data-origin-height=&quot;298&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/R2cMG/dJMcahrLGLC/REsAWIK4Dcv7GvQxY1Ko21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/R2cMG/dJMcahrLGLC/REsAWIK4Dcv7GvQxY1Ko21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/R2cMG/dJMcahrLGLC/REsAWIK4Dcv7GvQxY1Ko21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FR2cMG%2FdJMcahrLGLC%2FREsAWIK4Dcv7GvQxY1Ko21%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1144&quot; height=&quot;298&quot; data-origin-width=&quot;1144&quot; data-origin-height=&quot;298&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;* 만약&amp;nbsp; ImportError : DLL load failed while importing _pywrap_profiler 이라는 오류가 발생한 경우, 애플리케이션 제어 정책에서 파일을 차단하는 경우이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 윈도우 검색 &amp;gt; 스마트 앱 컨트롤 &amp;gt; 끄기로 설정이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;tensor라는 언어를 베이스로 개발된 keras라는 언어도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 역시 설치를 진행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pip install keras&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;spyder 등에서 import tensorflow as tf, import keras 등을 통해 설치가 제대로 진행되었는지 확인할 수 있다.&lt;/p&gt;</description>
      <category>아이티윌_데이터 분석 55기/강의내용_딥러닝</category>
      <author>ecosso</author>
      <guid isPermaLink="true">https://ecosso.tistory.com/142</guid>
      <comments>https://ecosso.tistory.com/142#entry142comment</comments>
      <pubDate>Thu, 23 Jul 2026 16:21:06 +0900</pubDate>
    </item>
    <item>
      <title>#7 7일차_반복 제어문, sed, awk</title>
      <link>https://ecosso.tistory.com/141</link>
      <description>&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;01 반복 제어문&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;02 sed - 비대화형 편집기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;03 awk - 자료처리&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-01 산술연산&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-02 grep&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-03 cut&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;01 반복 제어문&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- continue&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;특정 조건을 만나면 반복문 스킵, 다음 대상부터 다시 반복문을 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;for fname in $(ls *.sh)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;do&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; if [ $fname = &quot;a.sh&quot; ]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; then&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; exit 1&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; echo &quot;$fname&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;done&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;echo &quot;프로그램 종료&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;exit 0&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vi 22_continue.sh&lt;/p&gt;
&lt;pre id=&quot;code_1784769516699&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/sh

for i in $(seq 1 10)
do
  echo &quot;숫자 출력&quot;
  if [ $i -eq 5 ]
  then
    continue
  fi
  echo &quot;현재번호 : $i&quot;
done&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1784769567755&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9$ sh 22_continue.sh
숫자 출력
현재번호 : 1
숫자 출력
현재번호 : 2
숫자 출력
현재번호 : 3
숫자 출력
현재번호 : 4
숫자 출력
숫자 출력
현재번호 : 6
숫자 출력
현재번호 : 7
숫자 출력
현재번호 : 8
숫자 출력
현재번호 : 9
숫자 출력
현재번호 : 10&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;break 실습으로 변경해보았다.&lt;/p&gt;
&lt;pre id=&quot;code_1784769702579&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/sh

for i in $(seq 1 10)
do
  echo &quot;숫자 출력&quot;
  if [ $i -eq 5 ]
  then
    break
  fi
  echo &quot;현재번호 : $i&quot;
done
echo &quot;프로그램 종료&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1784769708428&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9$ sh 22_continue.sh
숫자 출력
현재번호 : 1
숫자 출력
현재번호 : 2
숫자 출력
현재번호 : 3
숫자 출력
현재번호 : 4
숫자 출력
프로그램 종료&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vi 23_menu.sh&lt;/p&gt;
&lt;pre id=&quot;code_1784771007139&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/sh

# env
dir=/home/itwill/linux_ex/ch8
todate=`date +%Y%m%d`

# function def
f_menu() {
  echo &quot;메뉴를 입력하세요(p:prior, q:quit) ☞ \c&quot;
  read ans2
  case $ans2 in 
    p|P)
      echo &quot;이전 메뉴로 돌아가겠습니다\c&quot;
      for i in . . .
      do
        echo &quot;$i\c&quot;
        sleep 1
      done
      contitue
      ;;
    q|Q)
      echo &quot;프로그램을 종료하겠습니다\c&quot;
      for i in . . .
      do
        echo &quot;$i\c&quot;
        sleep 1
      done
      break
      ;;
    *)
      echo &quot;잘못 입력되었습니다. 프로그램을 종료합니다.&quot;
      exit 1
      ;;
  esac
}

f_wait() {
  for i in . . .
  do
    echo &quot;$i\c&quot;
    sleep 1
  done
  echo
}

# main shell
clear

while [ 1 ]
do
  clear
  echo &quot;┌─────────────────────────┐&quot;
  echo &quot;│       관리자 화면       │&quot;
  echo &quot;└─────────────────────────┘&quot;
  echo &quot;┌─────────────────────────┐&quot;
  echo &quot;│  1. 유저 확인           │&quot;
  echo &quot;│  2. 파일 확인           │&quot;
  echo &quot;└─────────────────────────┘&quot;
  echo
  echo &quot;메뉴를 입력하세요(q:quit) ☞ \c&quot;
  read ans

  case $ans in
    1)
      echo &quot;유저 확인 화면으로 전환됩니다\c&quot;
      f_wait              # 대기 프롬프트 출력

      clear
      echo &quot;┌─────────────────────────┐&quot;
      echo &quot;│        유저 확인        │&quot;
      echo &quot;└─────────────────────────┘&quot;
      echo 

      echo &quot;유저명을 입력하세요 ☞ \c&quot;
      read username

      if grep -w ^$username /etc/passwd &amp;gt; /dev/null
      then
        hdir=`grep -w ^$username /etc/passwd | cut -d: -f6`
        shell=`grep -w ^$username /etc/passwd | cut -d: -f7`
        echo
        echo &quot;〓〓〓〓〓〓 결과 〓〓〓〓〓〓&quot;
        echo
        echo &quot;사용자명   : $username&quot;
        echo &quot;홈디렉토리 : $hdir&quot;
        echo &quot;로긴쉘     : $shell&quot;
        echo
        echo &quot;〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓&quot;
        echo
        f_menu                # 이전으로 돌아가기, 프로그램 종료 메뉴 출력
      else
        echo &quot;$username 유저가 존재하지 않습니다.&quot;
        exit 1
      fi
      ;;
    2) 
      echo &quot;파일 확인 화면으로 전환됩니다\c&quot;
      f_wait              # 대기 프롬프트 출력

      clear
      echo &quot;┌─────────────────────────┐&quot;
      echo &quot;│        파일 확인        │&quot;
      echo &quot;└─────────────────────────┘&quot;
      echo 

      echo &quot;파일명을 입력하세요 ☞ \c &quot;
      read fname
      
      if [ -f $fname ]
      then
        fsize=`ls -l $fname | cut -d&quot; &quot; -f5`
        fown=`ls -l $fname | cut -d&quot; &quot; -f3`
        echo
        echo &quot;〓〓〓〓〓〓 결과 〓〓〓〓〓〓&quot;
        echo
        echo &quot;파일명 : $fname&quot;
        echo &quot;소유자 : $fown&quot;
        echo &quot;크기   : $fsize&quot;
        echo
        echo &quot;〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓&quot;
        echo
        f_menu
      else
        echo &quot;$fname 파일이 존재하지 않습니다.&quot;
      fi
      ;;
    q|Q)
      echo &quot;프로그램을 종료하겠습니다\c&quot;
      f_wait
      break
      ;;
    *)
      echo &quot;잘못된 입력입니다.&quot;
      echo &quot;프로그램을 종료하겠습니다\c&quot;
      f_wait
      exit 1
      ;;
  esac
done

echo &quot;프로그램 정상 종료&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1784771061467&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;┌─────────────────────────┐
│       관리자 화면       │
└─────────────────────────┘
┌─────────────────────────┐
│  1. 유저 확인           │
│  2. 파일 확인           │
└─────────────────────────┘

메뉴를 입력하세요(q:quit) ☞ 2

┌─────────────────────────┐
│        파일 확인        │
└─────────────────────────┘

파일명을 입력하세요 ☞ /home/itwill/linux_ex/ch9/23_menu.sh

〓〓〓〓〓〓 결과 〓〓〓〓〓〓

파일명 : /home/itwill/linux_ex/ch9/23_menu.sh
소유자 : itwill
크기   : 3696

〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓

메뉴를 입력하세요(p:prior, q:quit) ☞&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;02 sed - 비대화형 편집기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동시에 여러 파일을 vi 편집기 없이 수정이 가능한 기능이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명령어로 파일 편집이 가능하다 (글자입력, 행 삭제, 글자 치환 등)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 사용법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sed [옵션] '명령어' 파일명&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 명령어&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;읽기(r)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력(p)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;삭제(d)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력(i,a)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;치환(s)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 옵션&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-n : 원하는 라인만 출력&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-e : 다중편집&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;d)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;set '5d' test.txt&amp;nbsp; -&amp;gt; 5번행 삭제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sed '1,5d' &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;test.txt&amp;nbsp; -&amp;gt; 1~5행 삭제&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;sed '/root'd'&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;test.txt&amp;nbsp; -&amp;gt; root가 포함된 행 삭제&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;sed 'root/!d'&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;test.txt&amp;nbsp; -&amp;gt; root가 포함되지 않은 행 삭제&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;sed '/^#/d'&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;test.txt&amp;nbsp; -&amp;gt; 주석처리된 행 삭제&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;sed /^$/d' &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;test.txt&amp;nbsp; -&amp;gt; 빈줄 삭제&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1784772955707&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9$ cat -n /etc/passwd | head -10
     1  root:x:0:0:root:/root:/bin/bash
     2  daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
     3  bin:x:2:2:bin:/bin:/usr/sbin/nologin
     4  sys:x:3:3:sys:/dev:/usr/sbin/nologin
     5  sync:x:4:65534:sync:/bin:/bin/sync
     6  games:x:5:60:games:/usr/games:/usr/sbin/nologin
     7  man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
     8  lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
     9  mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
    10  news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
    
itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9$ cat -n /etc/passwd | head -10 &amp;gt; test.txt

itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9$ sed '5d' test.txt
     1  root:x:0:0:root:/root:/bin/bash
     2  daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
     3  bin:x:2:2:bin:/bin:/usr/sbin/nologin
     4  sys:x:3:3:sys:/dev:/usr/sbin/nologin
     6  games:x:5:60:games:/usr/games:/usr/sbin/nologin
     7  man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
     8  lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
     9  mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
    10  news:x:9:9:news:/var/spool/news:/usr/sbin/nologin


itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9$ sed '1,5d' test.txt
     6  games:x:5:60:games:/usr/games:/usr/sbin/nologin
     7  man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
     8  lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
     9  mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
    10  news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9$

itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9$ sed '/root/d' test.txt
     2  daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
     3  bin:x:2:2:bin:/bin:/usr/sbin/nologin
     4  sys:x:3:3:sys:/dev:/usr/sbin/nologin
     5  sync:x:4:65534:sync:/bin:/bin/sync
     6  games:x:5:60:games:/usr/games:/usr/sbin/nologin
     7  man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
     8  lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
     9  mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
    10  news:x:9:9:news:/var/spool/news:/usr/sbin/nologin&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주석처리된 라인도 삭제할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1784773237115&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9$ cat test.txt
     1  root:x:0:0:root:/root:/bin/bash
     2  daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
     3  bin:x:2:2:bin:/bin:/usr/sbin/nologin
     4  sys:x:3:3:sys:/dev:/usr/sbin/nologin
#     5 sync:x:4:65534:sync:/bin:/bin/sync
#     6 games:x:5:60:games:/usr/games:/usr/sbin/nologin
     7  man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
     8  lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
     9  mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
    10  news:x:9:9:news:/var/spool/news:/usr/sbin/nologin

itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9$ sed '/^#/d' test.txt
     1  root:x:0:0:root:/root:/bin/bash
     2  daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
     3  bin:x:2:2:bin:/bin:/usr/sbin/nologin
     4  sys:x:3:3:sys:/dev:/usr/sbin/nologin
     7  man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
     8  lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
     9  mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
    10  news:x:9:9:news:/var/spool/news:/usr/sbin/nologin&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;p)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;sed '5p' test.txt -&amp;gt; 5번 행이 한 번 더 출력&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;sed -n '5p' 'test.txt -&amp;gt; 5번행만 출력&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;sed -n '/root/p/ test.txt -&amp;gt; root 단어가 포함된 라인만 출력&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;sed -n '/^$/p' test.txt | wc -l -&amp;gt; 빈 줄의 수 출력&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1784774292339&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9$ set '5p' test.txt
itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9$ sed -n '5p' test.txt
#     5 sync:x:4:65534:sync:/bin:/bin/sync&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;s)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sed 's/nologin//' test.txt -&amp;gt; 전체 범위에서 nologin 단어 삭제&lt;br /&gt;sed '2s/nologin//' test.txt -&amp;gt; 2번 행의 nologin 단어 삭제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sed '2,5s/nologin//' test.txt -&amp;gt; 2~5번 행의 nologin 단어 삭제 &lt;br /&gt;sed '2,5!s/nologin//' test.txt -&amp;gt; 2~5번 행을 제외한 나머지 행에서 nologin 단어 삭제&lt;br /&gt;sed '3s/bin/BIN/' test.txt -&amp;gt; 2~5번 행을 제외한 나머지 행에서 nologin 단어 삭제&lt;br /&gt;&lt;span&gt;sed '3s/bin/BIN/' test.txt -&amp;gt; 3번행에서 bin을 BIN으로 치환(첫번째 단어만 치환됨)&lt;br /&gt;sed '3s/bin/BIN/g' test.txt -&amp;gt; 3번행에서 bin을 BIN으로 치환(모든 단어가 치환됨)&lt;/span&gt; &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;sed '1,2s/%/#3/' text.txt -&amp;gt; 1~2번행 주척처리&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;sed '5,6s/^#//' test.txt -&amp;gt; 5~6번행 주석해지&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;sed '1,3s/^/&amp;nbsp; /' test.txt -&amp;gt; 1~3번행 두 칸 들여쓰기&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1784774765738&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9$ cat test.txt
     1  root:x:0:0:root:/root:/bin/bash
     2  daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
     3  bin:x:2:2:bin:/bin:/usr/sbin/nologin
     4  sys:x:3:3:sys:/dev:/usr/sbin/nologin
#     5 sync:x:4:65534:sync:/bin:/bin/sync
#     6 games:x:5:60:games:/usr/games:/usr/sbin/nologin
     7  man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
     8  lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
     9  mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
    10  news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
    
itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9$ sed 's/nologin//' test.txt
     1  root:x:0:0:root:/root:/bin/bash
     2  daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/
     3  bin:x:2:2:bin:/bin:/usr/sbin/
     4  sys:x:3:3:sys:/dev:/usr/sbin/
#     5 sync:x:4:65534:sync:/bin:/bin/sync
#     6 games:x:5:60:games:/usr/games:/usr/sbin/
     7  man:x:6:12:man:/var/cache/man:/usr/sbin/
     8  lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/
     9  mail:x:8:8:mail:/var/mail:/usr/sbin/
    10  news:x:9:9:news:/var/spool/news:/usr/sbin/
    
itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9$ sed '2s/nologin//' test.txt
     1  root:x:0:0:root:/root:/bin/bash
     2  daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/
     3  bin:x:2:2:bin:/bin:/usr/sbin/nologin
     4  sys:x:3:3:sys:/dev:/usr/sbin/nologin
#     5 sync:x:4:65534:sync:/bin:/bin/sync
#     6 games:x:5:60:games:/usr/games:/usr/sbin/nologin
     7  man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
     8  lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
     9  mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
    10  news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
    
itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9$ sed '2,5!s/nologin//' test.txt
     1  root:x:0:0:root:/root:/bin/bash
     2  daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
     3  bin:x:2:2:bin:/bin:/usr/sbin/nologin
     4  sys:x:3:3:sys:/dev:/usr/sbin/nologin
#     5 sync:x:4:65534:sync:/bin:/bin/sync
#     6 games:x:5:60:games:/usr/games:/usr/sbin/
     7  man:x:6:12:man:/var/cache/man:/usr/sbin/
     8  lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/
     9  mail:x:8:8:mail:/var/mail:/usr/sbin/
    10  news:x:9:9:news:/var/spool/news:/usr/sbin/
    
itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9$ sed '3s/bin/BIN/' test.txt
     1  root:x:0:0:root:/root:/bin/bash
     2  daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
     3  BIN:x:2:2:bin:/bin:/usr/sbin/nologin
     4  sys:x:3:3:sys:/dev:/usr/sbin/nologin
#     5 sync:x:4:65534:sync:/bin:/bin/sync
#     6 games:x:5:60:games:/usr/games:/usr/sbin/nologin
     7  man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
     8  lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
     9  mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
    10  news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
    
itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9$ sed '3s/bin/BIN/g' test.txt
     1  root:x:0:0:root:/root:/bin/bash
     2  daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
     3  BIN:x:2:2:BIN:/BIN:/usr/sBIN/nologin
     4  sys:x:3:3:sys:/dev:/usr/sbin/nologin
#     5 sync:x:4:65534:sync:/bin:/bin/sync
#     6 games:x:5:60:games:/usr/games:/usr/sbin/nologin
     7  man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
     8  lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
     9  mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
    10  news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
    
    itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9$ sed '5,6s/^#//' test.txt
     1  root:x:0:0:root:/root:/bin/bash
     2  daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
     3  bin:x:2:2:bin:/bin:/usr/sbin/nologin
     4  sys:x:3:3:sys:/dev:/usr/sbin/nologin
     5  sync:x:4:65534:sync:/bin:/bin/sync
     6  games:x:5:60:games:/usr/games:/usr/sbin/nologin
     7  man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
     8  lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
     9  mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
    10  news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
    
itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9$ sed '8,9s/^/  /' test.txt
     1  root:x:0:0:root:/root:/bin/bash
     2  daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
     3  bin:x:2:2:bin:/bin:/usr/sbin/nologin
     4  sys:x:3:3:sys:/dev:/usr/sbin/nologin
#     5 sync:x:4:65534:sync:/bin:/bin/sync
#     6 games:x:5:60:games:/usr/games:/usr/sbin/nologin
     7  man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
       8        lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
       9        mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
    10  news:x:9:9:news:/var/spool/news:/usr/sbin/nologin&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;i,a)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sed '5i\==========' test.txt -&amp;gt; 5번 라인에 '=========='를 입력&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sed '5a\==========' test.txt -&amp;gt; 5번 라인 밑에 '=========='를 입력&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;r)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sed '3r file.txt' test.txt -&amp;gt; test.txt 파일 3번행 다음 행에 file.txt 내용을 불러오기&lt;/p&gt;
&lt;pre id=&quot;code_1784775474859&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9$ echo &quot;abcde......&quot; -&amp;gt; file.txt
itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9$ echo &quot;abcde......&quot; &amp;gt;&amp;gt; file.txt
itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9$ cat file.txt
abcde...... -
abcde......

itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9$ sed '3r file.txt' test.txt
     1  root:x:0:0:root:/root:/bin/bash
     2  daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
     3  bin:x:2:2:bin:/bin:/usr/sbin/nologin
abcde...... -
abcde......
     4  sys:x:3:3:sys:/dev:/usr/sbin/nologin
#     5 sync:x:4:65534:sync:/bin:/bin/sync
#     6 games:x:5:60:games:/usr/games:/usr/sbin/nologin
     7  man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
     8  lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
     9  mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
    10  news:x:9:9:news:/var/spool/news:/usr/sbin/nologin&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;e)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sed -e '/^#/d' -e 's/bin//' test.txt -&amp;gt;&amp;nbsp;&lt;br /&gt;sed -e '/^#/d' -e 's'bin''g' test.txt -&amp;gt;&lt;/p&gt;
&lt;pre id=&quot;code_1784775732082&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9$ sed '/^#/d' test.txt
     1  root:x:0:0:root:/root:/bin/bash
     2  daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
     3  bin:x:2:2:bin:/bin:/usr/sbin/nologin
     4  sys:x:3:3:sys:/dev:/usr/sbin/nologin
     7  man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
     8  lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
     9  mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
    10  news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
    
itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9$ sed -e '/^#/d' -e 's/bin//' test.txt
     1  root:x:0:0:root:/root://bash
     2  daemon:x:1:1:daemon:/usr/s:/usr/sbin/nologin
     3  :x:2:2:bin:/bin:/usr/sbin/nologin
     4  sys:x:3:3:sys:/dev:/usr/s/nologin
     7  man:x:6:12:man:/var/cache/man:/usr/s/nologin
     8  lp:x:7:7:lp:/var/spool/lpd:/usr/s/nologin
     9  mail:x:8:8:mail:/var/mail:/usr/s/nologin
    10  news:x:9:9:news:/var/spool/news:/usr/s/nologin&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 실습하기&lt;/p&gt;
&lt;pre id=&quot;code_1784775944698&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9$ mkdir data
itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9$ cd data
itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9/data$ cp /etc/services services1
itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9/data$ cp /etc/services services2
itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9/data$ cp /etc/services services3
itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9/data$ cp /etc/services services4
itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9/data$ cp /etc/services services5

itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9/data$ head -10 services1
# Network services, Internet style
#
# Updated from https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml .
#
# New ports will be added on request if they have been officially assigned
# by IANA and used in the real-world or are needed by a debian package.
# If you need a huge list of used numbers please install the nmap package.

tcpmux          1/tcp                           # TCP port service multiplexer
echo            7/tcp&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vi&amp;nbsp;24_sed.sh &lt;/p&gt;
&lt;pre id=&quot;code_1784777535810&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/binsh

# data 디렉토리 하위 모든 파일을 1~7행 삭제 후 저장

maindir=/home/itwill/linux_ex/ch9/data

flist=$(find $maindir -maxdepth 1 -type f)

for fname in $flist
do
  echo &quot;$fname 파일을 수정하겠습니다.&quot;
  sed '1,7d $fname &amp;gt; $fname_imsi
  mv ${fname}_imsi $fname
done&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;03 awk - 자료처리&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 사용법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;awk [옵션] '[/검색] {명령어}' 파일&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-01 산술연산&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$ echo | awk '{print 1+2}'&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-02 grep&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;awk /itwill/ /etc/passwd&lt;/p&gt;
&lt;pre id=&quot;code_1784778178507&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9$ echo | awk '{print 1 + 2}'
3
itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9$ awk /itwill/ /etc/passwd
itwill:x:1000:1000:itwill:/home/itwill:/bin/bash
itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9$ grep itwill /etc/passwd
itwill:x:1000:1000:itwill:/home/itwill:/bin/bash
itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9$ echo abced | cut -c1,3
ac
itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9$ echo abcde | cut -c1-3
abc&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-03 cut&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;echo &quot;1:2:3&quot; | awk -F: '{print $1}' -&amp;gt; :으로 필드를 구분하여 첫 번째 필드 출력&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;echo &quot;1:2:3&quot; | awk -F: '{print $NF}' -&amp;gt; 마지막 필드 출력&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;echo &quot;1:2:3&quot; | awk -F: '{print $(NF-1)}' -&amp;gt; 마지막 이전 필드 출력&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;echo &quot;1:2:3&quot; | awk -F: '{print $1&quot;...&quot;$2}' -&amp;gt; print로 위치변수와 일반문자열을 함께 출력할 수 있음 (&quot;&quot;로 일반 문자열을 묶어 전달)&lt;/p&gt;
&lt;pre id=&quot;code_1784778176161&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9/data$ echo &quot;1:2:3&quot; | awk -F: '{print $1}'
1

itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9/data$ echo &quot;1:2:3&quot; | awk -F: '{print $NF}'
3

itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9/data$ echo &quot;1:2:3&quot; | awk -F: '{print $(NF-1)}'
2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;** 참고 : 외부 변수 값 전달&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;username=itwill&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;awk -F: '/$username/ {print $NF}' /etc/passwd -&amp;gt; 아무것도 출력되지 않는다. (홑따옴표 안의 $는 변수호출로 해석이 불가능)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;awk -F: '/'$username'/ {print $NF}' /etc/passwd -&amp;gt; 정상출력&lt;/p&gt;
&lt;pre id=&quot;code_1784778495834&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9/data$ awk -F: '/itwill/ {print $NF}' /etc/passwd
/bin/bash

itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9/data$ username=itwill
itwill@itwill-VMware-Virtual-Platform:~/linux_ex/ch9/data$ awk -F: '/$username/ {print $NF}' /etc/passwd&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>아이티윌_데이터 분석 55기/강의내용_Linux</category>
      <author>ecosso</author>
      <guid isPermaLink="true">https://ecosso.tistory.com/141</guid>
      <comments>https://ecosso.tistory.com/141#entry141comment</comments>
      <pubDate>Thu, 23 Jul 2026 12:54:38 +0900</pubDate>
    </item>
    <item>
      <title>#6 6일차_파이썬 스크립트 실행, 로깅 처리, 정렬, 특수기호 정의</title>
      <link>https://ecosso.tistory.com/140</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;01 리눅스에서 파이썬 스크립트 실행하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;02 로깅 처리&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-01 echo&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-02 tee&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;03 정렬&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-01&amp;nbsp; ls 결과 정렬&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-02 sort&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;04 특수기호 정의&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-01 * 과 ?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-02 []&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-03 ~와 -&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-04 ''와 &quot;&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-05 ; 과 |&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;05 논리 연산자&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-01 and 연산자&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-02 or 연산자&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-03 not 연산자&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;01 리눅스에서 파이썬 스크립트 실행하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;os 프롬포트에서 파이썬 프롬프트로 명령어 전달을 수행할 수 있다. (입력 프롬프트를 사용)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;ipython 사용 시 쓸데없는 헤더들을 출력하고 싶지 않다면 --no-banner와 함께 python prompt로 진입하는 것도 좋다.&lt;/p&gt;
&lt;pre id=&quot;code_1784681979757&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(base) itwill@ubuntu:~$ cds
(base) itwill@ubuntu:~/linux_ex/ch9$ ipython
Python 3.12.7 | packaged by Anaconda, Inc. | (main, Oct  4 2024, 13:27:36) [GCC 11.2.0]
Type 'copyright', 'credits' or 'license' for more information
IPython 8.27.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: exit

(base) itwill@ubuntu:~/linux_ex/ch9$ ipython --no-banner

In [1]:&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vi&amp;nbsp;19_ipython.sh&lt;/p&gt;
&lt;pre id=&quot;code_1784682491940&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/sh

echo &quot;파이썬 스크립트를 시작하겠습니다...&quot;
sleep 2

maindir=/home/itwill/linux_ex/ch9

ipython --no-banner --no-confirm-exit &amp;lt;&amp;lt; _eof_
import pandas as pd
emp = pd.read_csv(&quot;$maindir/emp.csv&quot;)
mean_sal = round(emp.loc[emp['DEPTNO'] == 10, 'SAL'].mean())
print(mean_sal)
_eof_

echo &quot;파이썬 스크립트를 종료하겠습니다...&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉘스크립트와 파이썬이 섞여있다. 따라서 end of file이라는 뜻의 _eof_로 파이썬 스크립트의 시작과 끝을 지정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(_eof_의 경우 원하는 형태로 작성 가능하다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$ ipython --no -banner -- no-confirm-exit &amp;lt;&amp;lt; _eof&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;python 명령어...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;_eof_&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;** 참고 : 리눅스에서 한글 깨짐 해결 (인코딩 변경)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$ iconv -f CP949 -t UTF8 card_history.csv &amp;gt; card_history_new.csv&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;** 참고 : os 프롬포트에서 sql 프롬포트로 명령어 전달하기&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;$ sqlplus -s scott/oracle &amp;lt;&amp;lt; _eof_&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;select ...;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;_eof_&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$ R &amp;lt;&amp;lt; _eof_&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;R 명령어...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;_eof_&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1784682713764&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(base) itwill@ubuntu:~/linux_ex/ch9$ sh 19_ipython.sh
파이썬 스크립트를 시작하겠습니다...

In [1]:
In [2]:
In [3]:
In [4]: 2917

In [5]: 파이썬 스크립트를 종료하겠습니다...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vi 19_ipython.sh를 card 관련 코드로 변경해본다.&lt;/p&gt;
&lt;pre id=&quot;code_1784683057972&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/sh

echo &quot;파이썬 스크립트를 시작하겠습니다...&quot;
sleep 2

maindir=/home/itwill/linux_ex/ch9

ipython --no-banner --no-confirm-exit &amp;lt;&amp;lt; _eof_  &amp;gt; /dev/null
import pandas as pd
card = pd.read_csv('$maindir/card_history.csv')
card = card.drop(columns='NUM')
card = card.map(lambda x : int(x.replace(',','')))
card.sum().to_csv(&quot;$maindir/total.csv&quot;, header=False)
_eof_

echo
echo &quot;파이썬 스크립트를 종료하겠습니다...&quot;

if [ -f $maindir/total.csv ]
then
  echo &quot;분석 스크립트 정상 수행 완료&quot;
  cat $maindir/total.csv
else
  echo &quot;스크립트 실행 오류 발생&quot;
fi&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1784683244005&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(base) itwill@ubuntu:~/linux_ex/ch9$ sh 19_ipython.sh
파이썬 스크립트를 시작하겠습니다...

파이썬 스크립트를 종료하겠습니다...
분석 스크립트 정상 수행 완료
식료품,795500
의복,3947700
외식비,281400
책값,945000
온라인소액결제,186300
의료비,633600&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ffffff; background-color: #666666;&quot;&gt;&lt;b&gt;[ 연습문제 - 20_ipython2.sh ]&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;** 참고 : 리눅스에서 한글 깨짐 해결 (인코딩 변경)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;$ iconv -f CP949 -t UTF8 delivery.csv &amp;gt; delivery_new.csv&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;$ delivery_new.csv delivery.csv&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;delivery.csv 파일을 읽고 시간대별배달건수가 가장 많은 업종의 이름을 출력 후 result.csv로 저장.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;저장 후 결과 출력하기.&lt;/p&gt;
&lt;pre id=&quot;code_1784686406550&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[ 내 답변 ]

#!/bin/sh

echo &quot;파이썬 스크립트를 시작하겠습니다...&quot;
sleep 2

maindir=/home/itwill/linux_ex/ch9

ipython --no-banner --no-confirm-exit &amp;lt;&amp;lt; _eof_ &amp;gt; /dev/null
import pandas as pd
delivery = pd.read_csv('$maindir/delivery_new.csv')
delivery = delivery.drop(columns=['시도','시군구','읍면동'])
df = delivery.groupby([&quot;시간대&quot;,&quot;업종&quot;])[&quot;통화건수&quot;].sum()
result = df.groupby(level=0).idxmax()
result = result.apply(lambda x: x[1])
result.name = &quot;업종&quot;
result.index.name = &quot;시간대&quot;
result.to_csv(&quot;$maindir/result.csv&quot;, header=False)
_eof_

echo
echo &quot;파이썬 스크립트를 종료하겠습니다...&quot;

if [ -f $maindir/result.csv ]
then
  echo &quot;분석 스크립트 정상 수행 완료&quot;
  cat $maindir/result.csv
else
  echo &quot;스크립트 실행 오류 발생&quot;
fi

(base) itwill@ubuntu:~/linux_ex/ch9$ sh 20_ipython2.sh
파이썬 스크립트를 시작하겠습니다...

파이썬 스크립트를 종료하겠습니다...
분석 스크립트 정상 수행 완료
0,치킨
1,치킨
2,음식점-중국음식
3,음식점-중국음식
4,음식점-중국음식
5,음식점-중국음식
6,음식점-중국음식
7,음식점-중국음식
8,음식점-중국음식
9,음식점-중국음식
10,음식점-중국음식
11,음식점-중국음식
12,음식점-중국음식
13,음식점-중국음식
14,음식점-중국음식
15,음식점-중국음식
16,음식점-중국음식
17,치킨
18,치킨
19,치킨
20,치킨
21,치킨
22,치킨
23,치킨&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1784688577926&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[ 문제풀이 ] + 여러가지 옵션 
#!/bin/sh

# env
maindir=/home/itwill/linux_ex/ch9
logdir=$maindir/log

if [ ! -d $logdir ]
then
  mkdir -p $logdir
fi

# main
echo &quot;파이썬 분석 프로그램 실행\c&quot;
for i in . . .
do
  echo &quot;${i}\c&quot;
  sleep 1
done
echo

# 파이썬 프롬프트 실행
start_time=$(date +%s)                           # 시작시간 체크
echo &quot;시작 시간 : $(date '+%Y/%m/%d %H:%M:%S')&quot;

ipython --no-banner --no-confirm-exit &amp;lt;&amp;lt; _eof_ &amp;gt; /dev/null
import pandas as pd
df = pd.read_csv(&quot;$maindir/delivery.csv&quot;)
df_new = df.groupby(['시간대','업종'])['통화건수'].sum().reset_index()
df_new['최대통화건수'] = df_new.groupby('시간대')['통화건수'].transform('max')
result = df_new.query('최대통화건수 == 통화건수')[['시간대','업종']]
result.to_csv(&quot;$maindir/result.csv&quot;, index=False)
_eof_

end_time=$(date +%s)                              # 종료시간 체크
elapsed=$(expr $end_time - $start_time)           # 경과시간 계산

echo &quot;파이썬 분석 프로그램 종료&quot;
echo &quot;종료 시간 : $(date '+%Y/%m/%d %H:%M:%S')&quot;
echo &quot;소요 시간: ${elapsed}초&quot;
echo &quot;출력건수 : $(cat $maindir/result.csv | wc -l) 건&quot;

# 분석 결과 체크
if [ -f $maindir/result.csv ]
then
  echo &quot;분석 스크립트 정상 수행 완료&quot;
  echo &quot;┌────────────────────────────┐&quot;
  echo &quot;│       분석 결과 출력        │&quot;
  echo &quot;└────────────────────────────┘&quot;
  cat $maindir/result.csv
else
  echo &quot;스크립트 실행 오류 발생&quot;
fi

# 분석 결과 삭제
rm $maindir/result.csv

(base) itwill@ubuntu:~/linux_ex/ch9$ sh 20_ipython2.sh
파이썬 분석 프로그램 실행...
시작 시간 : 2026/07/22 11:50:17
파이썬 분석 프로그램 종료
종료 시간 : 2026/07/22 11:50:18
소요 시간: 1초
출력건수 : 24 건
분석 스크립트 정상 수행 완료
┌────────────────────────────┐
│       분석 결과 출력        │
└────────────────────────────┘
0,치킨
1,치킨
2,음식점-중국음식
3,음식점-중국음식
4,음식점-중국음식
5,음식점-중국음식
6,음식점-중국음식
7,음식점-중국음식
8,음식점-중국음식
9,음식점-중국음식
10,음식점-중국음식
11,음식점-중국음식
12,음식점-중국음식
13,음식점-중국음식
14,음식점-중국음식
15,음식점-중국음식
16,음식점-중국음식
17,치킨
18,치킨
19,치킨
20,치킨
21,치킨
22,치킨
23,치킨&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;02 로깅 처리&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-01 echo&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;if [ ! -f ...]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; then&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; echo &quot;파일이 존재하지 않습니다.&quot; &amp;gt;&amp;gt; test.log&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fi&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;echo &quot;프로그램 시작시간&quot; &amp;gt;&amp;gt; test.log&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-02 tee&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;표준출력(모니터)과 특정 파일에 동시 출력이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉,&amp;nbsp;tee는 백그라운드에서 실행도 되지만 화면에 동시에 실행결과도 출력해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 사용법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;echo &quot;문자열&quot; | tee [옵션]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 옵션&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-a : 이어쓰기&lt;/p&gt;
&lt;pre id=&quot;code_1784690189823&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(base) itwill@ubuntu:~/linux_ex/ch9$ echo &quot;프로그램 시작&quot; &amp;gt;&amp;gt; test.log
(base) itwill@ubuntu:~/linux_ex/ch9$ cat test.log
프로그램 시작

(base) itwill@ubuntu:~/linux_ex/ch9$ echo &quot;프로그램 종료&quot; | tee -a test.log
프로그램 종료

(base) itwill@ubuntu:~/linux_ex/ch9$ cat test.log
프로그램 시작
프로그램 종료&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vi 20_ipython2.sh&lt;/p&gt;
&lt;pre id=&quot;code_1784692928152&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/sh

# env
maindir=/home/itwill/linux_ex/ch9
logdir=$maindir/log
todate=$(date +%Y%m%d)

if [ ! -d $logdir ]
then
  mkdir -p $logdir
fi

# main
echo &quot;파이썬 분석 프로그램 실행&quot; | tee -a ${logdir}/python_${todate}.log
#for i in . . .
#do
#  echo &quot;${i}\c&quot;
#  sleep 1
#done
#echo

# 파이썬 프롬프트 실행
start_time=$(date +%s)                           # 시작시간 체크
echo &quot;시작 시간 : $(date '+%Y/%m/%d %H:%M:%S')&quot; | tee -a ${logdir}/python_${todate}.log

ipython --no-banner --no-confirm-exit &amp;lt;&amp;lt; _eof_ &amp;gt; /dev/null
import pandas as pd
df = pd.read_csv(&quot;$maindir/delivery.csv&quot;)
df_new = df.groupby(['시간대','업종'])['통화건수'].sum().reset_index()
df_new['최대통화건수'] = df_new.groupby('시간대')['통화건수'].transform('max')
result = df_new.query('최대통화건수 == 통화건수')[['시간대','업종']]
result.to_csv(&quot;$maindir/result.csv&quot;, index=False)
_eof_

end_time=$(date +%s)                              # 종료시간 체크
elapsed=$(expr $end_time - $start_time)           # 경과시간 계산

echo &quot;파이썬 분석 프로그램 종료&quot; | tee -a ${logdir}/python_${todate}.log
echo &quot;종료 시간 : $(date '+%Y/%m/%d %H:%M:%S')&quot; | tee -a ${logdir}/python_${todate}.log
echo &quot;소요 시간: ${elapsed}초&quot; | tee -a ${logdir}/python_${todate}.log
echo &quot;출력건수 : $(cat $maindir/result.csv | wc -l) 건&quot; | tee -a ${logdir}/python_${todate}.log

# 분석 결과 체크
if [ -f $maindir/result.csv ]
then
  echo &quot;분석 스크립트 정상 수행 완료&quot; | tee -a ${logdir}/python_${todate}.log
  echo &quot;┌────────────────────────────┐&quot; | tee -a ${logdir}/python_${todate}.log
  echo &quot;│       분석 결과 출력        │&quot; | tee -a ${logdir}/python_${todate}.log
  echo &quot;└────────────────────────────┘&quot; | tee -a ${logdir}/python_${todate}.log
  cat $maindir/result.csv | tee -a ${logdir}/python_${todate}.log
else
  echo &quot;스크립트 실행 오류 발생&quot; | tee -a ${logdir}/python_${todate}.log
fi

# 분석 결과 삭제
rm $maindir/result.csv

(base) itwill@ubuntu:~/linux_ex/ch9$ sh 20_ipython2.sh
파이썬 분석 프로그램 실행
시작 시간 : 2026/07/22 13:01:23
파이썬 분석 프로그램 종료
종료 시간 : 2026/07/22 13:01:24
소요 시간: 1초
cat: /home/itwill/linux_ex/ch9/result.csv: No such file or directory
출력건수 : 0 건
스크립트 실행 오류 발생
rm: cannot remove '/home/itwill/linux_ex/ch9/result.csv': No such file or directory&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;03 정렬&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-01&amp;nbsp; ls 결과 정렬&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 정렬 옵션&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-v : 자연정렬 (파일/디렉토리명에 숫자가 포함된 경우 숫자 기준으로 정렬)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-t : 시간정렬 (최근 수정 파일이 상단에 출력됨)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-r : 역순정렬&lt;/p&gt;
&lt;pre id=&quot;code_1784693054639&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(base) itwill@ubuntu:~/linux_ex/ch9$ ls -lv
total 14016
-rwxr--r-- 1 itwill itwill     377 Jul 16 11:34 1_grep.sh
-rwxr--r-- 1 itwill itwill     242 Jul 16 12:00 2_cut.sh
-rwxr--r-- 1 itwill itwill     480 Jul 16 14:54 3_wc.sh
-rw-rw-r-- 1 itwill itwill     303 Jul 16 15:58 4_if1.sh
-rw-rw-r-- 1 itwill itwill     721 Jul 20 10:44 5_backup.sh
-rw-rw-r-- 1 itwill itwill     767 Jul 20 11:26 6_case.sh
-rw-rw-r-- 1 itwill itwill     389 Jul 20 12:15 7_head.sh
-rwxrwxr-x 1 itwill itwill      79 Jul 20 14:40 8_cron.sh
-rw-rw-r-- 1 itwill itwill     390 Jul 20 19:07 9_for1.sh
-rw-rw-r-- 1 itwill itwill     725 Jul 20 19:35 10_for2.sh
-rw-rw-r-- 1 itwill itwill     354 Jul 20 20:00 11_expr.sh
-rw-rw-r-- 1 itwill itwill     251 Jul 20 20:03 12_bc.sh
-rw-rw-r-- 1 itwill itwill     320 Jul 20 20:39 13_color.sh
-rw-rw-r-- 1 itwill itwill     597 Jul 21 12:50 14_text_color.sh
-rw-rw-r-- 1 itwill itwill     119 Jul 21 12:58 15_export1.sh
-rw-rw-r-- 1 itwill itwill      80 Jul 21 12:57 16_export2.sh
-rw-rw-r-- 1 itwill itwill     154 Jul 21 15:08 17_python.py
-rw-rw-r-- 1 itwill itwill     232 Jul 21 15:35 18_python.py
-rw-rw-r-- 1 itwill itwill     610 Jul 22 10:20 19_ipython.sh
-rw-rw-r-- 1 itwill itwill    2147 Jul 22 13:01 20_ipython2.sh
drwxrwxr-x 2 itwill itwill    4096 Jul 20 10:28 backup
-rw-rw-r-- 1 itwill itwill    1721 Mar 30 10:07 card_history.csv
-rw-rw-r-- 1 itwill itwill     320 Jul 21 12:04 color.env
-rw-rw-r-- 1 itwill itwill   38493 Jul 22 13:04 cron.log
-rw-rw-r-- 1 itwill itwill 6027546 Mar 30 10:07 delivery.csv
-rw-rw-r-- 1 itwill itwill 7914024 Jul 22 11:10 delivery_new.csv
-rw-rw-r-- 1 itwill itwill     734 Mar 30 10:07 emp.csv
-rw-rw-r-- 1 itwill itwill  239690 Jul 21 14:16 index.html
drwxr--r-- 2 itwill itwill    4096 Jul 22 12:59 log
-rwxr--r-- 1 itwill itwill     161 Jul 16 12:14 sh_260716.log
-rw-rw-r-- 1 itwill itwill      40 Jul 22 12:16 test.log
-rwxr--r-- 1 itwill itwill       8 Jul 16 11:06 test.txt
-rw-rw-r-- 1 itwill itwill     109 Jul 21 15:36 total.csv&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-02 sort&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일 안의 내용을 정렬한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 사용법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sort [옵션] 파일명&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cat 파일명 | sort [옵션]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 옵션&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-n : 숫자 정렬&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-r : 역순 정렬&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-k : 필드 위치 지정 (ex. -k2 -&amp;gt; 두번째 필드값 기준으로 정렬)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-t : 필드 구분자 지정, 생략시 탭 (ex. -t, -&amp;gt; , 로 분리구분)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-u : 유일값 출력&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex)&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$ sort -t: -k1 -r /etc/passwd -&amp;gt; /etc/passwd 파일을 : 으로 구분하여 첫 번쨰 필드 기준으로 정렬 수행&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$ ls -l | sort -k -nr -&amp;gt; ls -l 결과를 탭으로 구분하여 다섯번째 필드 (파일크기) 역순 정렬&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$ tail -n +2 emp.csv | sort -t, -k3 -&amp;gt; emp.csv 파일 첫 줄을 제외한 대상을 세 번쨰 필드 순서대로 정렬&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$ cut -d, -f3 emp.csv | sort -u -&amp;gt; emp.csv 파일을 ,로 구분하여 세번째 필드의 유일값 출력&lt;/p&gt;
&lt;pre id=&quot;code_1784699055458&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(base) itwill@ubuntu:~/linux_ex/ch9$ head -3 /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin

(base) itwill@ubuntu:~/linux_ex/ch9$ sort -t: /etc/passwd
_apt:x:42:65534::/nonexistent:/usr/sbin/nologin
avahi:x:103:109:Avahi mDNS daemon:/run/avahi-daemon:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
_chrony:x:988:988:Chrony Daemon:/var/lib/chrony:/usr/sbin/nologin
colord:x:977:977:colord colour management daemon:/var/lib/colord:/usr/sbin/nologin
...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일 크기 순으로 정렬을 해보도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵션에 인수가 들어가는 경우는 한 번 띄워서 쓴다. (그렇지 않은 경우 붙여서 사용)&lt;/p&gt;
&lt;pre id=&quot;code_1784699361474&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(base) itwill@ubuntu:~/linux_ex/ch9$ ls -l | sort -k5 -n
total 14020
-rwxr--r-- 1 itwill itwill       8 Jul 16 11:06 test.txt
-rw-rw-r-- 1 itwill itwill      40 Jul 22 12:16 test.log
-rwxrwxr-x 1 itwill itwill      79 Jul 20 14:40 8_cron.sh
-rw-rw-r-- 1 itwill itwill      80 Jul 21 12:57 16_export2.sh
-rw-rw-r-- 1 itwill itwill     109 Jul 21 15:36 total.csv
-rw-rw-r-- 1 itwill itwill     119 Jul 21 12:58 15_export1.sh
-rw-rw-r-- 1 itwill itwill     154 Jul 21 15:08 17_python.py
-rwxr--r-- 1 itwill itwill     161 Jul 16 12:14 sh_260716.log
-rw-rw-r-- 1 itwill itwill     232 Jul 21 15:35 18_python.py
-rwxr--r-- 1 itwill itwill     242 Jul 16 12:00 2_cut.sh
-rw-rw-r-- 1 itwill itwill     251 Jul 20 20:03 12_bc.sh
-rw-rw-r-- 1 itwill itwill     303 Jul 16 15:58 4_if1.sh
-rw-rw-r-- 1 itwill itwill     320 Jul 20 20:39 13_color.sh
-rw-rw-r-- 1 itwill itwill     320 Jul 21 12:04 color.env
-rw-rw-r-- 1 itwill itwill     354 Jul 20 20:00 11_expr.sh
-rwxr--r-- 1 itwill itwill     377 Jul 16 11:34 1_grep.sh
-rw-rw-r-- 1 itwill itwill     389 Jul 20 12:15 7_head.sh
-rw-rw-r-- 1 itwill itwill     390 Jul 20 19:07 9_for1.sh
-rwxr--r-- 1 itwill itwill     480 Jul 16 14:54 3_wc.sh
-rw-rw-r-- 1 itwill itwill     597 Jul 21 12:50 14_text_color.sh
-rw-rw-r-- 1 itwill itwill     610 Jul 22 10:20 19_ipython.sh
-rw-rw-r-- 1 itwill itwill     721 Jul 20 10:44 5_backup.sh
-rw-rw-r-- 1 itwill itwill     725 Jul 20 19:35 10_for2.sh
-rw-rw-r-- 1 itwill itwill     734 Mar 30 10:07 emp.csv
-rw-rw-r-- 1 itwill itwill     767 Jul 20 11:26 6_case.sh
-rw-rw-r-- 1 itwill itwill    1721 Mar 30 10:07 card_history.csv
-rw-rw-r-- 1 itwill itwill    2147 Jul 22 13:01 20_ipython2.sh
drwxr--r-- 2 itwill itwill    4096 Jul 22 12:59 log
drwxrwxr-x 2 itwill itwill    4096 Jul 20 10:28 backup
-rw-rw-r-- 1 itwill itwill   43428 Jul 22 14:49 cron.log
-rw-rw-r-- 1 itwill itwill  239690 Jul 21 14:16 index.html
-rw-rw-r-- 1 itwill itwill 6027546 Mar 30 10:07 delivery.csv
-rw-rw-r-- 1 itwill itwill 7914024 Jul 22 11:10 delivery_new.csv&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1784699447227&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(base) itwill@ubuntu:~/linux_ex/ch9$ ls -l | sort -k5 -nr
-rw-rw-r-- 1 itwill itwill 7914024 Jul 22 11:10 delivery_new.csv
-rw-rw-r-- 1 itwill itwill 6027546 Mar 30 10:07 delivery.csv
-rw-rw-r-- 1 itwill itwill  239690 Jul 21 14:16 index.html
-rw-rw-r-- 1 itwill itwill   43475 Jul 22 14:50 cron.log
drwxrwxr-x 2 itwill itwill    4096 Jul 20 10:28 backup
drwxr--r-- 2 itwill itwill    4096 Jul 22 12:59 log
-rw-rw-r-- 1 itwill itwill    2147 Jul 22 13:01 20_ipython2.sh
-rw-rw-r-- 1 itwill itwill    1721 Mar 30 10:07 card_history.csv
-rw-rw-r-- 1 itwill itwill     767 Jul 20 11:26 6_case.sh
-rw-rw-r-- 1 itwill itwill     734 Mar 30 10:07 emp.csv
-rw-rw-r-- 1 itwill itwill     725 Jul 20 19:35 10_for2.sh
-rw-rw-r-- 1 itwill itwill     721 Jul 20 10:44 5_backup.sh
-rw-rw-r-- 1 itwill itwill     610 Jul 22 10:20 19_ipython.sh
-rw-rw-r-- 1 itwill itwill     597 Jul 21 12:50 14_text_color.sh
-rwxr--r-- 1 itwill itwill     480 Jul 16 14:54 3_wc.sh
-rw-rw-r-- 1 itwill itwill     390 Jul 20 19:07 9_for1.sh
-rw-rw-r-- 1 itwill itwill     389 Jul 20 12:15 7_head.sh
-rwxr--r-- 1 itwill itwill     377 Jul 16 11:34 1_grep.sh
-rw-rw-r-- 1 itwill itwill     354 Jul 20 20:00 11_expr.sh
-rw-rw-r-- 1 itwill itwill     320 Jul 21 12:04 color.env
-rw-rw-r-- 1 itwill itwill     320 Jul 20 20:39 13_color.sh
-rw-rw-r-- 1 itwill itwill     303 Jul 16 15:58 4_if1.sh
-rw-rw-r-- 1 itwill itwill     251 Jul 20 20:03 12_bc.sh
-rwxr--r-- 1 itwill itwill     242 Jul 16 12:00 2_cut.sh
-rw-rw-r-- 1 itwill itwill     232 Jul 21 15:35 18_python.py
-rwxr--r-- 1 itwill itwill     161 Jul 16 12:14 sh_260716.log
-rw-rw-r-- 1 itwill itwill     154 Jul 21 15:08 17_python.py
-rw-rw-r-- 1 itwill itwill     119 Jul 21 12:58 15_export1.sh
-rw-rw-r-- 1 itwill itwill     109 Jul 21 15:36 total.csv
-rw-rw-r-- 1 itwill itwill      80 Jul 21 12:57 16_export2.sh
-rwxrwxr-x 1 itwill itwill      79 Jul 20 14:40 8_cron.sh
-rw-rw-r-- 1 itwill itwill      40 Jul 22 12:16 test.log
-rwxr--r-- 1 itwill itwill       8 Jul 16 11:06 test.txt&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1784699838427&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(base) itwill@ubuntu:~/linux_ex/ch9$ cut -d, -f3 emp.csv | sort -u
ANALYST
CLERK
JOB
MANAGER
PRESIDENT
SALESMAN&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1784702178051&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(base) itwill@ubuntu:~/linux_ex/ch9$ tail -n +2 emp.csv | sort -t, -k3
7902,FORD,ANALYST,7566,1981-12-03 0:00,3000,,20
7788,SCOTT,ANALYST,7566,1987-04-17 0:00,3000,,20
7900,JAMES,CLERK,7698,1981-12-03 0:00,950,,30
7934,MILLER,CLERK,7782,1982-01-23 0:00,1300,,10
7876,ADAMS,CLERK,7788,1987-05-23 0:00,1100,,20
7369,SMITH,CLERK,7902,1980-12-17 0:00,800,,20
7566,JONES,MANAGER,7839,1981-04-02 0:00,2975,,20
7698,BLAKE,MANAGER,7839,1981-05-01 0:00,2850,,30
7782,CLARK,MANAGER,7839,1981-06-09 0:00,2450,,10
7839,KING,PRESIDENT,,1981-11-17 0:00,5000,,10
7499,ALLEN,SALESMAN,7698,1981-02-20 0:00,1600,300,30
7844,TURNER,SALESMAN,7698,1981-09-08 0:00,1500,0,30
7654,MARTIN,SALESMAN,7698,1981-09-28 0:00,1250,1400,30
7521,WARD,SALESMAN,7698,1982-02-22 0:00,1250,500,30&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 특정 단어가 존재하는 행을 제외하고 grep&lt;/p&gt;
&lt;pre id=&quot;code_1784702273931&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(base) itwill@ubuntu:~/linux_ex/ch9$ grep -v EMPNO emp.csv
7369,SMITH,CLERK,7902,1980-12-17 0:00,800,,20
7499,ALLEN,SALESMAN,7698,1981-02-20 0:00,1600,300,30
7521,WARD,SALESMAN,7698,1982-02-22 0:00,1250,500,30
7566,JONES,MANAGER,7839,1981-04-02 0:00,2975,,20
7654,MARTIN,SALESMAN,7698,1981-09-28 0:00,1250,1400,30
7698,BLAKE,MANAGER,7839,1981-05-01 0:00,2850,,30
7782,CLARK,MANAGER,7839,1981-06-09 0:00,2450,,10
7788,SCOTT,ANALYST,7566,1987-04-17 0:00,3000,,20
7839,KING,PRESIDENT,,1981-11-17 0:00,5000,,10
7844,TURNER,SALESMAN,7698,1981-09-08 0:00,1500,0,30
7876,ADAMS,CLERK,7788,1987-05-23 0:00,1100,,20
7900,JAMES,CLERK,7698,1981-12-03 0:00,950,,30
7902,FORD,ANALYST,7566,1981-12-03 0:00,3000,,20
7934,MILLER,CLERK,7782,1982-01-23 0:00,1300,,10&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;04 특수기호 정의&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-01 * 과 ?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- * : 자리수에 상관없이 모든&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-? : 한 자리수의 모든&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$ ls *.sh&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$ ls a*b -&amp;gt; abb, accccccb 출력&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$ ls a?b -&amp;gt; abb 출력 (세자리 파일명만 출력)&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-02 []&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 자리에 표현되는 모든 값 전달 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$ ls a[123].txt -&amp;gt; a1.txt, a2.txt, a3.txt 모두 출력&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-03 ~와 -&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$ cd ~ -&amp;gt; 홈디렉토리로 이동&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$ cd - -&amp;gt; 이전 디렉토리로 이동&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-04 ''와 &quot;&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt; 일반 문자열을 감쌀 때는 구분하지 않음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt; '' 안의 $, '', \, *가 무시됨 (기호 그대로를 출력함)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$ var1=&quot;abcde&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$ echo &quot;$var1&quot; -&amp;gt; abcde가 출력됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$ echo '$var1' -&amp;gt; $var1이 출력됨&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-05 ; 과 |&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt; ; : 명령어의 연속 실행&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt; | : 이전 명령어의 결과를 다음 명령어의 인수로 전달&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$ var1=1 ; var2 =2&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$ ls -l | grep -v ^- -&amp;gt; ls -l 결과에서 -으로 시작하는 라인을 제외하고 출력&lt;/p&gt;
&lt;pre id=&quot;code_1784700679723&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(base) itwill@ubuntu:~/linux_ex/ch9$ cp *.sh backup

(base) itwill@ubuntu:~/linux_ex/ch9$ ls
10_for2.sh        17_python.py    4_if1.sh     card_history.csv  log
11_expr.sh        18_python.py    5_backup.sh  color.env         sh_260716.log
12_bc.sh          19_ipython.sh   6_case.sh    cron.log          test.log
13_color.sh       1_grep.sh       7_head.sh    delivery.csv      test.txt
14_text_color.sh  20_ipython2.sh  8_cron.sh    delivery_new.csv  total.csv
15_export1.sh     2_cut.sh        9_for1.sh    emp.csv
16_export2.sh     3_wc.sh         backup       index.html

(base) itwill@ubuntu:~/linux_ex/ch9$ mkdir test
(base) itwill@ubuntu:~/linux_ex/ch9$ cd test
(base) itwill@ubuntu:~/linux_ex/ch9/test$ touch a1.txt a2.txt a3.txt b1.txt b2.txt b3.txt
(base) itwill@ubuntu:~/linux_ex/ch9/test$ ls
a1.txt  a2.txt  a3.txt  b1.txt  b2.txt  b3.txt

(base) itwill@ubuntu:~/linux_ex/ch9/test$ ls [ab]1.txt
a1.txt  b1.txt
(base) itwill@ubuntu:~/linux_ex/ch9/test$ ls a[123].txt
a1.txt  a2.txt  a3.txt
(base) itwill@ubuntu:~/linux_ex/ch9/test$ ls a?.txt
a1.txt  a2.txt  a3.txt


(base) itwill@ubuntu:~/linux_ex/ch9/test$ ls a?.txt
a1.txt  a2.txt  a3.txt
(base) itwill@ubuntu:~/linux_ex/ch9/test$ ls a*.txt
a1.txt  a2.txt  a3.txt  aaa.txt&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1784701083779&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(base) itwill@ubuntu:~/linux_ex/ch9/test$ echo &quot;abcde&quot;
abcde
(base) itwill@ubuntu:~/linux_ex/ch9/test$ echo 'abcde'
abcde

(base) itwill@ubuntu:~/linux_ex/ch9/test$ var1=abcde
(base) itwill@ubuntu:~/linux_ex/ch9/test$ echo $var1
abcde

(base) itwill@ubuntu:~/linux_ex/ch9/test$ echo &quot;변수명 : $var1&quot;
변수명 : abcde
(base) itwill@ubuntu:~/linux_ex/ch9/test$ echo '변수명 : $var1'
변수명 : $var1&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;05 논리 연산자&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-01 and 연산자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;if [ $var1 -ge 1 -a $var1 -le 100 ] # -a 가 and 연산자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;if [ $var1 -ge 1 ] &amp;amp;&amp;amp; [ $var1 -le 100 ]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vi&amp;nbsp;21_and.sh&lt;/p&gt;
&lt;pre id=&quot;code_1784704357985&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;#!/bin/sh

var1=100

#if [ $var1 -ge 50 -a $var -le 100 ]
if [ $var1 -ge 50 ] &amp;amp;&amp;amp; [ $var1 -le 100 ]
then
  echo &quot; 50 &amp;lt;= x &amp;lt;=100&quot;
else
  echo &quot;False&quot;
fi

(base) itwill@ubuntu:~/linux_ex/ch9$ sh 21_and.sh
 50 &amp;lt;= x &amp;lt;=100&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-02 or 연산자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;if [ $var1 -ge 1 -o $var1 -le 100 ]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;if [ $var1 -ge 1 ] || [ $var1 -le 100 ]&lt;/p&gt;</description>
      <category>아이티윌_데이터 분석 55기/강의내용_Linux</category>
      <author>ecosso</author>
      <guid isPermaLink="true">https://ecosso.tistory.com/140</guid>
      <comments>https://ecosso.tistory.com/140#entry140comment</comments>
      <pubDate>Wed, 22 Jul 2026 16:13:35 +0900</pubDate>
    </item>
  </channel>
</rss>