<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>tech_n0.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Sat, 10 May 2025 07:45:44 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>tech_n0.log</title>
            <url>https://velog.velcdn.com/images/tech_n0/profile/8ce3b2f6-9d9f-40ac-9395-cdaaf29c9b78/social_profile.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. tech_n0.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/tech_n0" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Unreal - 상호작용, 게이트와 버튼]]></title>
            <link>https://velog.io/@tech_n0/Unreal-%EC%83%81%ED%98%B8%EC%9E%91%EC%9A%A9-%EA%B2%8C%EC%9D%B4%ED%8A%B8%EC%99%80-%EB%B2%84%ED%8A%BC</link>
            <guid>https://velog.io/@tech_n0/Unreal-%EC%83%81%ED%98%B8%EC%9E%91%EC%9A%A9-%EA%B2%8C%EC%9D%B4%ED%8A%B8%EC%99%80-%EB%B2%84%ED%8A%BC</guid>
            <pubDate>Sat, 10 May 2025 07:45:44 GMT</pubDate>
            <description><![CDATA[<p>프로젝트 전 언리얼 써본적 없으니 연습부터...!</p>
<hr>
<h1 id="1-상호작용-키e-구현">1. 상호작용 키(E) 구현</h1>
<ol>
<li><p><code>/Interfaces</code> 에Input Action <code>IA_Interact</code>  생성 후 벨류 타입을 부울로 설정
<img src="https://velog.velcdn.com/images/tech_n0/post/1c9eda47-cfdb-49d2-9a43-31a544be6de4/image.png" alt="">
<img src="https://velog.velcdn.com/images/tech_n0/post/54bda2a1-4919-49ee-b2f1-c7e3828c5b34/image.png" alt=""></p>
</li>
<li><p>IMC_Default 의 맵핑에 IA_Interact 를 추가해주고 E키를 등록</p>
<p> <img src="https://velog.velcdn.com/images/tech_n0/post/d9098833-6d71-4840-8146-0de4beb8e9e5/image.png" alt=""></p>
</li>
<li><p>블루프린트 인터페이스 BPI_Interact 생성</p>
</li>
</ol>
<ul>
<li>함수명 Interact  생성 후 인풋 추가
<img src="https://velog.velcdn.com/images/tech_n0/post/7fb7ae79-1263-4d18-8afc-98c90c5c8d4b/image.png" alt=""></li>
</ul>
<ol start="4">
<li>BP_FirstPersonCharacter 이벤트 그래프 구현
<img src="https://velog.velcdn.com/images/tech_n0/post/223a28a7-c83e-431c-b78e-272ee5e00441/image.png" alt=""></li>
</ol>
<p>실행후 E를 누르면 디버깅용 라인 트레이스가 나옴
<img src="https://velog.velcdn.com/images/tech_n0/post/2c979d1f-c05f-41f6-a73f-336d64d25b95/image.png" alt=""></p>
<hr>
<h1 id="게이트와-버튼--만들기">게이트와 버튼  만들기</h1>
<p>일단 세트장을 만들었다.
<img src="https://velog.velcdn.com/images/tech_n0/post/3590d364-f691-437d-b25b-5c3c7bc8205a/image.png" alt=""></p>
<p>To do: 책장에 있는 파란 책을 인터렉션 하면 문이 없어짐</p>
<p>TMI: 문 구현이 ㅠ어려운거 같아 일단 프롭이 없어지는것으로 하고 나중에 문 구현하기로…</p>
<ul>
<li><p>결계로 쓸 블루프린트 클래스 (엑터 상속) 생성
<img src="https://velog.velcdn.com/images/tech_n0/post/6a9fd9fa-9062-43e4-ad97-452523abd4a2/image.png" alt=""></p>
</li>
<li><p>컴포넌트에 스태틱 매쉬를 만들고 사라지게할 프롭을 할당
<img src="https://velog.velcdn.com/images/tech_n0/post/0bc981fa-f151-422f-867f-bc08b38a8655/image.png" alt=""></p>
</li>
<li><p>이벤트 그래프에서 커스텀 이벤트를 만들자
<img src="https://velog.velcdn.com/images/tech_n0/post/a1fd9d35-56a8-4da0-a325-c12cb38f961a/image.png" alt=""></p>
</li>
</ul>
<ul>
<li>결계 제거 버튼으로 쓸 블루프린트 클래스 (엑터 상속) 생성
<img src="https://velog.velcdn.com/images/tech_n0/post/a767f854-59c6-432a-8690-716d7a4076e0/image.png" alt=""></li>
</ul>
<ul>
<li>같은 방법으로 스테틱 매쉬로 쓸 프롭을 설정해줌
<img src="https://velog.velcdn.com/images/tech_n0/post/ee061c74-4a95-4e8f-b017-c8cb1273cc9f/image.png" alt=""></li>
</ul>
<ul>
<li><p>클래스 세팅 → Implemented Interfaces → BPI_Interact 추가
<img src="https://velog.velcdn.com/images/tech_n0/post/3943610b-ccff-447e-b8e6-d9bebf64173c/image.png" alt=""></p>
</li>
<li><p>변수 하나 만들고 아래와 같이 참조변수(BP_Barrier)로
<img src="https://velog.velcdn.com/images/tech_n0/post/49a3543a-836c-4ee2-b7af-d731bfa5068b/image.png" alt=""></p>
</li>
</ul>
<blockquote>
<p>저 설정이 뭔가?</p>
<h3 id="1-instance-editable">1. Instance Editable</h3>
<ul>
<li><strong>설명</strong>: 에디터의 Details 패널에서 해당 변수를 노출해서, 씬에 배치된 <strong>인스턴스마다</strong> 값을 다르게 설정할 수 있게 해 줍니다.</li>
<li><strong>용도</strong>:<ul>
<li><strong>레벨 디자이너</strong>가 월드에 배치한 액터마다 색상, 크기, 속도 등의 값을 조정해야 할 때</li>
<li>같은 블루프린트 클래스로 만든 여러 오브젝트가 약간씩 다른 초기값을 가져야 할 때</li>
</ul>
</li>
<li><strong>작동 위치</strong>:<ol>
<li>블루프린트 에디터에서 변수 선택</li>
<li>Details 패널 → <strong>Instance Editable</strong> 체크</li>
<li>레벨 뷰포트나 World Outliner 에서 액터 선택 시, Details 패널에 변수 슬롯이 생겨 값을 변경할 수 있음</li>
</ol>
</li>
</ul>
<hr>
<h3 id="2-expose-on-spawn">2. Expose On Spawn</h3>
<ul>
<li><p><strong>설명</strong>: 액터를 <strong>Spawn Actor From Class</strong> 노드로 생성할 때, 해당 변수를 <strong>스폰 핀</strong>으로 노출시켜 스폰 시점에 값을 지정하게 해 줍니다.</p>
</li>
<li><p><strong>용도</strong>:</p>
<ul>
<li>런타임에 액터를 동적으로 생성하면서, 생성 시점에만 결정되는 초기 파라미터(예: 목표 좌표, 주인 정보)를 넘겨주고 싶을 때</li>
<li>생성 후에 따로 세터 함수를 호출하지 않고, 스폰 노드 하나로 초기화와 생성을 동시에 처리하려 할 때</li>
</ul>
</li>
<li><p><strong>작동 위치</strong>:</p>
<ol>
<li><p>블루프린트 에디터에서 변수 선택</p>
</li>
<li><p>Details 패널 → <strong>Expose On Spawn</strong> 체크</p>
</li>
<li><p>다른 블루프린트나 레벨 블루프린트에서 <strong>Spawn Actor From Class</strong> 노드를 배치하면,</p>
<p> 해당 변수 이름으로 핀이 자동으로 추가되어 값을 연결할 수 있음</p>
</li>
</ol>
</li>
</ul>
</blockquote>
<ul>
<li><p>Event Interact 에 IsValid로 타깃 베리어를 검사해서 리무브 베리어를 호출
<img src="https://velog.velcdn.com/images/tech_n0/post/e0f0ef46-cd6d-42ae-b318-3b69de173daa/image.png" alt=""></p>
</li>
<li><p>다시 BP_FirstPersonCharacter 로
<img src="https://velog.velcdn.com/images/tech_n0/post/9f96c6f4-b598-40ef-b9f0-32ee2e6c20f8/image.png" alt="">
인터페이스에 BPI Interact를 선택</p>
</li>
</ul>
<hr>
<aside>

<p>ㅋㅋㅋㅋㅋㅋ 계속 작동을 안해서 고쳐보려고 쌩 쑈를했는데</p>
<p>책장안에 스위치로 넣을 책을 넣었는데 책장 충돌 범위가 책을 덮어버려서 였음….</p>
<p>이미 작동하는건데 고치려고 바보짓 30분함 ㅋㅋㅋㅋㅋ</p>
</aside>

<p><img src="https://velog.velcdn.com/images/tech_n0/post/c1e38b28-b35f-40d2-892a-3b810205e2ed/image.png" alt=""></p>
<p>근데 여기엔 문제가있다. 콜리전만 언에이블 해서 문 모양은 계속 그대로 남아있다.</p>
<ul>
<li>블루프린트 베리어의 이벤트 그래프에 setvisibility 노드를 연결해주면 해결
<img src="https://velog.velcdn.com/images/tech_n0/post/b4153d44-03db-4d75-bf72-1278fef0dee9/image.png" alt=""></li>
</ul>
<p>근데 그냥 뿅하고 사라진다.</p>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/1dc45027-4aaf-4792-ac1f-3780d069ae28/image.gif" alt=""></p>
<p>너무 짜친다…</p>
<p>페이드 아웃을 넣어보자</p>
<hr>
<h2 id="물체가-사라지는-페이드-아웃">물체가 사라지는 페이드 아웃</h2>
<p>메터리얼을 투명 머터리얼로 수정하자</p>
<ul>
<li>디테일에서 Blend Mode 를 Opaque(불투명)에서 Translucent(투명)로 변경</li>
<li>Scalar Parameter노드를 추가하고 이름: Opacity, 값 0.1로 한 뒤 Opacity와 연결
<img src="https://velog.velcdn.com/images/tech_n0/post/c3db6ad1-36e9-42f8-b009-3c10710be890/image.png" alt=""></li>
</ul>
<ul>
<li><p><strong>BP_Barrier</strong> 에 변수 추가</p>
<ul>
<li>이름: <code>DynMat</code></li>
<li>타입: <strong>Material Instance Dynamic</strong></li>
</ul>
</li>
<li><p>이벤트 그래프의 BeginPlay에 Dynamic Material 구현
<img src="https://velog.velcdn.com/images/tech_n0/post/6eb3ca00-4a6b-4f2d-8217-6630edf1fbdf/image.png" alt=""></p>
</li>
</ul>
<ul>
<li>리무브 베리어 로직에 타임라인을 넣고 수정<ul>
<li><strong>Timeline</strong> 더블클릭 → 에디터 열기<ul>
<li><strong>Float Track</strong> 추가<ul>
<li>이름: <code>Alpha</code></li>
<li>길이(Length): 예 1.0초</li>
</ul>
</li>
<li>키포인트 두개 생성<ul>
<li>Time = 0.0, Value = 0.0</li>
<li>Time = 1.0, Value = 1.0
<img src="https://velog.velcdn.com/images/tech_n0/post/c14d5216-9a15-43d1-a2e8-e0ed0f5ec7a9/image.png" alt="">
<img src="https://velog.velcdn.com/images/tech_n0/post/15b3668d-3b93-443d-8d50-ac58dff5ca40/image.png" alt=""></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p><code>RemoveBarrier</code> 호출 시 바로 충돌이 꺼지고, Timeline을 따라 <code>Opacity</code>가 1→0으로 점점 줄어들며 메시가 사라짐</p>
<ul>
<li>사라진 엑터는 더이상 필요가 없음으로 효율을 위해 Destroy 해줌
<img src="https://velog.velcdn.com/images/tech_n0/post/34c9d459-76c1-4f90-ba13-6ac4464aef74/image.png" alt=""></li>
</ul>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/730154c0-15c0-497f-8d76-a56f3020332d/image.gif" alt=""></p>
<aside>

<h3 id="근데-타임라인에-play-play-from-start-는-뭐가-다르지">근데 타임라인에 <code>play</code>, <code>play from start</code> 는 뭐가 다르지?</h3>
<p>Timeline 컴포넌트에는 재생을 제어하는 두 가지 주요 노드가 있는데, 그 차이는 “현재 재생 위치(position)를 초기화하느냐 안 하느냐” 입니다:</p>
<ul>
<li><strong>Play</strong><ul>
<li>Timeline을 <strong>현재 위치</strong>(Timeline의 내부 커서)에서 앞으로 재생합니다.</li>
<li>이미 한 번 끝까지 재생돼서 커서가 Duration(끝) 지점에 있다면, Play를 호출해도 아무 동작이 일어나지 않습니다.</li>
<li>중간에 멈췄다면(예: Pause 후) 멈춘 지점부터 이어서 재생할 때 사용합니다.</li>
</ul>
</li>
<li><strong>Play from Start</strong><ul>
<li>Timeline의 내부 커서를 <strong>0초 지점</strong>(시작 위치)로 되돌린 뒤, 처음부터 재생합니다.</li>
<li>“항상 처음부터” 페이드아웃을 반복하고 싶을 때 이 노드를 써야 합니다.</aside>

</li>
</ul>
</li>
</ul>
<p><code>play</code>로 하면 이미 결계가 사라졌을때 또 버튼을 눌러도 반복은 안됨</p>
<p><code>play from start</code> 는 반복시 없어진 결계가 또 나타나서 다시 사라짐</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[비전 에이전트 - 파노라마]]></title>
            <link>https://velog.io/@tech_n0/%EB%B9%84%EC%A0%84-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8-%ED%8C%8C%EB%85%B8%EB%9D%BC%EB%A7%88</link>
            <guid>https://velog.io/@tech_n0/%EB%B9%84%EC%A0%84-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8-%ED%8C%8C%EB%85%B8%EB%9D%BC%EB%A7%88</guid>
            <pubDate>Fri, 09 May 2025 17:12:18 GMT</pubDate>
            <description><![CDATA[<p>!youtube[Y7C8qIpo7Dg?si=zqwrUWeOuw-I-tM2]</p>
<p>문제 발생</p>
<table>
<thead>
<tr>
<th>문제</th>
<th>해결</th>
</tr>
</thead>
<tbody><tr>
<td>카메라 연결 실패</td>
<td>기본 값인 0번 카메라가 아닌 1번 카메라를 할당</td>
</tr>
<tr>
<td>바로 종료 시 에러</td>
<td><code>quitFunction</code> 에서  <code>cap</code>이 있을 때만 <code>release</code> 하게 수정</td>
</tr>
<tr>
<td>- 코드</td>
<td></td>
</tr>
<tr>
<td><a href="https://github.com/techn-0/computer_vision_STD/blob/main/6-5.py">https://github.com/techn-0/computer_vision_STD/blob/main/6-5.py</a></td>
<td></td>
</tr>
<tr>
<td>```python</td>
<td></td>
</tr>
<tr>
<td>import cv2 as cv</td>
<td></td>
</tr>
<tr>
<td>import numpy as np</td>
<td></td>
</tr>
<tr>
<td>from PyQt5.QtWidgets import *</td>
<td></td>
</tr>
<tr>
<td>import sys</td>
<td></td>
</tr>
<tr>
<td>import winsound</td>
<td></td>
</tr>
</tbody></table>
<p>class Panorama(QMainWindow):
  def <strong>init</strong>(self):
    super().<strong>init</strong>()
    self.setWindowTitle(&quot;Panorama&quot;)
    self.setGeometry(300, 300, 800, 400)</p>
<pre><code>collectButton = QPushButton(&quot;영상 수집&quot;, self)
self.showButton = QPushButton(&quot;영상 보기&quot;, self)
self.stitchButton = QPushButton(&quot;영상 합성&quot;, self)
self.saveButton = QPushButton(&quot;영상 저장&quot;, self)
quitButton = QPushButton(&quot;종료&quot;, self)
self.label = QLabel(&quot;포스가 함꼐하길...&quot;, self)

collectButton.setGeometry(10, 40, 85, 50)
self.showButton.setGeometry(180, 40, 85, 50)
self.stitchButton.setGeometry(300, 40, 85, 50)
self.saveButton.setGeometry(380, 40, 85, 50)
quitButton.setGeometry(480, 40, 85, 50)
self.label.setGeometry(10, 80, 700, 200)

self.showButton.setEnabled(False)
self.stitchButton.setEnabled(False)
self.saveButton.setEnabled(False)

collectButton.clicked.connect(self.collectFunction)
self.showButton.clicked.connect(self.showFunction)
self.stitchButton.clicked.connect(self.stitchFunction)
self.saveButton.clicked.connect(self.saveFunction)
quitButton.clicked.connect(self.quitFunction)</code></pre><p>  def collectFunction(self):
    self.showButton.setEnabled(False)
    self.stitchButton.setEnabled(False)
    self.saveButton.setEnabled(False)
    self.label.setText(&#39;c를 여러번 눌러 수집, 끝나면 q로 비디오 종료&#39;)</p>
<pre><code>self.cap = cv.VideoCapture(1, cv.CAP_DSHOW) # 1번 카메라 사용으로 수정
if not self.cap.isOpened(): sys.exit(&#39;카메라 연결 실패&#39;)

self.imgs = []
while True:
  ret, frame = self.cap.read()
  if not ret: break

  cv.imshow(&#39;video display&#39;, frame)

  key = cv.waitKey(1)
  if key == ord(&#39;c&#39;):
    self.imgs.append(frame) #저장
  elif key == ord(&#39;q&#39;):
    self.cap.release()
    cv.destroyWindow(&#39;video display&#39;)
    break

if len(self.imgs) &gt;= 2:
  self.showButton.setEnabled(True)
  self.stitchButton.setEnabled(True)
  self.saveButton.setEnabled(True)</code></pre><p>  def showFunction(self):
    self.label.setText(&#39;수집 영상은&#39; + str(len(self.imgs)) + &#39;장&#39;)
    stack = cv.resize(self.imgs[0], dsize = (0, 0), fx = 0.3, fy= 0.3)
    for i in range(1, len(self.imgs)):
      stack = np.hstack((stack, cv.resize(self.imgs[i], dsize = (0, 0), fx = 0.3, fy = 0.3)))
    cv.imshow(&#39;Img collection&#39;, stack)</p>
<p>  def stitchFunction(self):
    stitcher = cv.Stitcher_create()
    status, self.img_stitched = stitcher.stitch(self.imgs)
    if status == cv.STITCHER_OK:
      cv.imshow(&#39;Img stitched panorama&#39;, self.img_stitched) 
    else:
      winsound.Beep(2000, 600)
      self.label.setText(&#39;파노라마 제작 실패&#39;)</p>
<p>  def saveFunction(self):
    fname = QFileDialog.getSaveFileName(self, &#39;파일저장&#39;, &#39;./&#39;)
    cv.imwrite(fname[0], self.img_stitched)</p>
<p>  def quitFunction(self):
    if hasattr(self, &#39;cap&#39;):  # cap이 있을 때만 release
        self.cap.release()
    cv.destroyAllWindows()
    self.close()</p>
<p>app = QApplication(sys.argv)
win = Panorama()
win.show()
app.exec_()</p>
<pre><code>
![](https://velog.velcdn.com/images/tech_n0/post/84e2ea5e-c773-4d44-9e65-986255bfb124/image.png)
![](https://velog.velcdn.com/images/tech_n0/post/6e752686-8563-4615-b070-f9f2101153ec/image.png)
![](https://velog.velcdn.com/images/tech_n0/post/f5a5136e-283d-4883-be71-f05c40de4a79/image.png)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[비전에이전트-포스터 인식]]></title>
            <link>https://velog.io/@tech_n0/%EB%B9%84%EC%A0%84%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8-%ED%8F%AC%EC%8A%A4%ED%84%B0-%EC%9D%B8%EC%8B%9D</link>
            <guid>https://velog.io/@tech_n0/%EB%B9%84%EC%A0%84%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8-%ED%8F%AC%EC%8A%A4%ED%84%B0-%EC%9D%B8%EC%8B%9D</guid>
            <pubDate>Fri, 09 May 2025 17:04:25 GMT</pubDate>
            <description><![CDATA[<ul>
<li>초기 코드</li>
</ul>
<p><a href="https://github.com/techn-0/computer_vision_STD/blob/main/6-4%EC%B4%88%EA%B8%B0%20%EC%BD%94%EB%93%9C.py">https://github.com/techn-0/computer_vision_STD/blob/main/6-4초기 코드.py</a>
<img src="https://velog.velcdn.com/images/tech_n0/post/2273e196-21ff-45cd-970b-c8dd1bdc448e/image.png" alt=""></p>
<p>문제 발생</p>
<table>
<thead>
<tr>
<th>문제</th>
<th>해결</th>
</tr>
</thead>
<tbody><tr>
<td>등록 포스터 이미지 사이즈 불일치</td>
<td><code>cv.resize()</code>를 사용하여 등록 포스터를 (240×320) 크기로 통일</td>
</tr>
<tr>
<td>매칭 수 높은 포스터 하나만 결과 출력</td>
<td>루프로 순회하여 모든 포스터 인식</td>
</tr>
<tr>
<td>부정확한 매칭 발생, 필터링 없음</td>
<td>T값을  0.4로 낮춰 부정확한 매칭 필터링, 좋은 매칭을 상위 30개로 제한, Homography 결과가 실패 또는 inlier 10개 미만이면 무시</td>
</tr>
</tbody></table>
<ul>
<li>최종 코드
<a href="https://github.com/techn-0/computer_vision_STD/blob/main/6-4.py">https://github.com/techn-0/computer_vision_STD/blob/main/6-4.py</a></li>
</ul>
<pre><code class="language-python">import cv2 as cv
import numpy as np
from PyQt5.QtWidgets import *
import sys
import winsound

class TrafficWeak(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle(&#39;영화찾기&#39;)
        self.setGeometry(300, 300, 800, 300)

        # 버튼 및 라벨 생성
        signButton = QPushButton(&#39;포스터 등록&#39;, self)
        roadButton = QPushButton(&#39;타겟 불러오기&#39;, self)
        recognitionButton = QPushButton(&#39;인식&#39;, self)
        quitButton = QPushButton(&#39;종료&#39;, self)
        self.label = QLabel(&#39;welcome&#39;, self)

        # 버튼 및 라벨 위치 설정
        signButton.setGeometry(10, 10, 100, 30)
        roadButton.setGeometry(110, 10, 100, 30)
        recognitionButton.setGeometry(210, 10, 100, 30)
        quitButton.setGeometry(510, 10, 100, 30)
        self.label.setGeometry(10, 50, 780, 250)

        # 버튼 클릭 이벤트 연결
        signButton.clicked.connect(self.signFunction)
        roadButton.clicked.connect(self.roadFunction)
        recognitionButton.clicked.connect(self.recognitionFunction)
        quitButton.clicked.connect(self.quitFunction)

        # 포스터 파일 경로 및 이름
        self.signFiles = [[&#39;토토로.jpg&#39;, &#39;토토로&#39;], [&#39;로보캅.jpg&#39;, &#39;로보캅&#39;], [&#39;메트로폴리스.jpg&#39;, &#39;메트로폴리스&#39;]]
        self.signImgs = []

    def signFunction(self):
        # 포스터 등록 함수
        self.label.clear()
        self.label.setText(&#39;포스터 등록&#39;)

        standard_size = (240, 320)  # 표준 포스터 크기

        for fname, _ in self.signFiles:
            img = cv.imread(fname)
            resized = cv.resize(img, standard_size)  # 크기 통일
            self.signImgs.append(resized)
            cv.imshow(fname, resized)

    def roadFunction(self):
        # 영화관 장면 이미지 불러오기
        if self.signImgs == []:
            self.label.setText(&#39;먼저 포스터 등록!&#39;)
        else:
            fname = QFileDialog.getOpenFileName(self, &#39;Open file&#39;, &#39;./&#39;)
            self.roadImg = cv.imread(fname[0])
            if self.roadImg is None:
                sys.exit(&#39;파일 찾을 수 없음&#39;)
            cv.imshow(&#39;Road scene&#39;, self.roadImg)

    def recognitionFunction(self):
        # 포스터 인식 함수
        if self.roadImg is None:
            self.label.setText(&#39;타겟 입력!&#39;)
            return

        sift = cv.SIFT_create()  # SIFT 객체 생성

        KD = []  # 등록 포스터들의 키포인트와 디스크립터 저장
        for img in self.signImgs:
            gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
            KD.append(sift.detectAndCompute(gray, None))

        # 장면 이미지의 키포인트 및 디스크립터 추출
        grayRoad = cv.cvtColor(self.roadImg, cv.COLOR_BGR2GRAY)
        road_kp, road_des = sift.detectAndCompute(grayRoad, None)

        matcher = cv.DescriptorMatcher_create(cv.DescriptorMatcher_FLANNBASED)

        display_img = self.roadImg.copy()  # 원본 훼손 방지용 복사본

        for i in range(len(KD)):
            sign_kp, sign_des = KD[i]
            knn_matches = matcher.knnMatch(sign_des, road_des, 2)

            # Lowe&#39;s ratio test 적용
            good_match = []
            for m, n in knn_matches:
                if m.distance &lt; 0.4 * n.distance:
                    good_match.append(m)

            if len(good_match) &lt; 10:
                continue  # 좋은 매칭이 충분하지 않으면 넘어감

            # 상위 30개 좋은 매칭만 사용
            good_match = sorted(good_match, key=lambda x: x.distance)[:30]

            # 매칭된 포인트 좌표 추출
            points1 = np.float32([sign_kp[m.queryIdx].pt for m in good_match])
            points2 = np.float32([road_kp[m.trainIdx].pt for m in good_match])

            # Homography 계산
            H, mask = cv.findHomography(points1, points2, cv.RANSAC, 3.0)
            if H is None or mask is None or np.sum(mask) &lt; 10:
                continue  # Homography 실패나 신뢰성 낮으면 무시

            # 포스터 외곽 박스 변환
            h1, w1 = self.signImgs[i].shape[:2]
            box = np.float32([[0, 0], [0, h1], [w1, h1], [w1, 0]]).reshape(-1, 1, 2)
            box_transformed = cv.perspectiveTransform(box, H)

            # 초록색 박스 그리기
            display_img = cv.polylines(display_img, [np.int32(box_transformed)], True, (0, 255, 0), 4)

            # 매칭 결과(drawMatches) 이미지 생성
            img_match = np.empty(
                (max(h1, display_img.shape[0]), w1 + display_img.shape[1], 3),
                dtype=np.uint8
            )

            cv.drawMatches(
                self.signImgs[i], sign_kp,
                display_img, road_kp,
                good_match, img_match,
                flags=cv.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS
            )

            cv.imshow(f&quot;Matches: {self.signFiles[i][1]}&quot;, img_match)

            # 인식 성공 표시 및 소리 알림
            self.label.setText(self.signFiles[i][1] + &#39; 영화 감지됨!&#39;)
            winsound.Beep(2000, 300)

    def quitFunction(self):
        # 창 종료 함수
        cv.destroyAllWindows()
        self.close()

# 프로그램 실행
app = QApplication(sys.argv)
window = TrafficWeak()
window.show()
app.exec_()</code></pre>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/9cb3c8e0-f665-4b45-a634-00b36b544d21/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/daf86351-ddd6-4db0-bd55-e1b190e69a9a/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/f50f21b0-fbcb-420f-9a8b-94c2170e2617/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/5575f9fb-dc34-4772-a217-6c99f95bb8a9/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/63d728e0-4bab-4fee-b3ee-0680ef57f3e8/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[838. Push Dominoes]]></title>
            <link>https://velog.io/@tech_n0/838.-Push-Dominoes</link>
            <guid>https://velog.io/@tech_n0/838.-Push-Dominoes</guid>
            <pubDate>Mon, 05 May 2025 07:03:41 GMT</pubDate>
            <description><![CDATA[<p><a href="https://leetcode.com/problems/push-dominoes/?envType=daily-question&amp;envId=2025-05-01">https://leetcode.com/problems/push-dominoes/?envType=daily-question&amp;envId=2025-05-01</a></p>
<blockquote>
<p>주어진 도미노 배열 문자열에서 &#39;.&#39;(무너지지 않은 도미노)에 양옆에서 밀리는 힘(&#39;L&#39;, &#39;R&#39;)이 작용하면, 이 도미노가 어느 방향으로 쓰러질지 구하는 문제</p>
</blockquote>
<ul>
<li>입력: <code>dominoes: str</code><ul>
<li>길이 <code>n</code>인 문자열, 각 문자는 <code>&#39;.&#39;</code>, <code>&#39;L&#39;</code>, <code>&#39;R&#39;</code> 중 하나</li>
</ul>
</li>
<li>출력: 최종 상태 문자열 (<code>&#39;.&#39;</code>, <code>&#39;L&#39;</code>, <code>&#39;R&#39;</code> 조합)</li>
</ul>
<hr>
<h2 id="📋-알고리즘-개요">📋 알고리즘 개요</h2>
<ol>
<li><strong>현재 상태</strong>(<code>cur</code>)를 <code>list</code>로 관리</li>
<li><strong>반복</strong>: 한 번의 라운드에서 쓰러질 도미노를 계산해서 <strong>새 배열</strong>(<code>new</code>)에 기록</li>
<li>더 이상 쓰러질 도미노가 없으면 반복 종료</li>
<li>각 라운드가 끝나면 <code>cur = new</code></li>
<li>최종 <code>cur</code>를 문자열로 합쳐서 반환</li>
</ol>
<hr>
<h2 id="💡-핵심-아이디어">💡 핵심 아이디어</h2>
<ul>
<li><strong>동시성 유지</strong>: 한 라운드 안에서 일어난 변화는 다음 라운드에만 반영되도록, <code>cur</code>와 <code>new</code>를 분리</li>
<li><strong>양방향 힘 계산</strong>:<ul>
<li>왼쪽에서 밀리는 힘 <code>LeftForce</code> → 바로 오른쪽 이웃(<code>i+1</code>)이 <code>&#39;L&#39;</code></li>
<li>오른쪽에서 밀리는 힘 <code>RightForce</code> → 바로 왼쪽 이웃(<code>i-1</code>)이 <code>&#39;R&#39;</code></li>
<li>두 힘이 동시에 오면 무시(<code>.</code> 유지)</li>
</ul>
</li>
<li><strong>반복 종료 조건</strong>: 한 라운드에서 쓰러진 도미노가 없다면(<code>changed == False</code>)</li>
</ul>
<pre><code class="language-python">class Solution:
    def pushDominoes(self, dominoes: str) -&gt; str:
        n = len(dominoes)
        cur = list(dominoes)
        while True:
            new = cur.copy()
            changed = False

            for i in range(n):
                # 이미쓰러진 벽돌
                if cur[i] != &#39;.&#39;:
                    continue
                # L방향 힘이 있으려면 i+1 벽돌이 있고 그 벽돌이 L 이여햐 함
                LeftForce = (i &lt; n-1   and cur[i+1] == &#39;L&#39;)
                # R방향 힘이 있으려면 i-1 벽돌이 있고 그 벽돌이 R 이여햐 함
                RighForce = (i &gt; 0  and cur[i-1] == &#39;R&#39;)

                if LeftForce and not RighForce:
                    new[i] = &#39;L&#39;
                    changed = True
                elif RighForce and not LeftForce :
                    new[i] = &#39;R&#39;
                    changed = True
            if changed == False:
                break
            cur = new
        return &#39;&#39;.join(cur)</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[2962. Count Subarrays Where Max Element Appears at Least K Times]]></title>
            <link>https://velog.io/@tech_n0/2962.-Count-Subarrays-Where-Max-Element-Appears-at-Least-K-Times</link>
            <guid>https://velog.io/@tech_n0/2962.-Count-Subarrays-Where-Max-Element-Appears-at-Least-K-Times</guid>
            <pubDate>Mon, 05 May 2025 06:58:53 GMT</pubDate>
            <description><![CDATA[<h1 id="문제">문제</h1>
<ul>
<li>링크: <a href="https://leetcode.com/problems/count-subarrays-where-max-element-appears-at-least-k-times/?envType=daily-question&amp;envId=2025-04-29">https://leetcode.com/problems/count-subarrays-where-max-element-appears-at-least-k-times/?envType=daily-question&amp;envId=2025-04-29</a></li>
</ul>
<p>요즘 슬라이딩 윈도우로만 풀다보니 다른 방법으로 풀기 귀찮아졌다</p>
<h1 id="코드">코드</h1>
<pre><code class="language-python">class Solution:
    def countSubarrays(self, nums: List[int], k: int) -&gt; int:
        count = 0
        maxCount = 0
        n = len(nums)
        maxN = max(nums)

        l = 0
        r = 0

        for r in range(n):
            if nums[r] == maxN:
                maxCount += 1
            while maxCount &gt;= k:
                count += n - r
                if nums[l] == maxN:
                    maxCount -= 1
                l += 1
        return count</code></pre>
<h2 id="설명">설명</h2>
<ul>
<li><p>조건(인풋 배열의 max값이 k번 이상 들어가있는 서브어레이인가)을 만족하는 서브배열의 갯수를 찾는 문제</p>
</li>
<li><p>슬라이딩 윈도우 기법으로 풀면 쉽게 풀 수 있다.</p>
</li>
<li><p>l과 r 모두 index 0 에서 시작</p>
</li>
<li><p>r이 배열의 최대값 원소와 같다면 maxCount를 1 더해줌</p>
</li>
<li><p>누적 maxCount가 k 값보다 많다면</p>
<ul>
<li>총 서브트리 개수인 count를  l부터 r까지로 만들수 있는 서브트리중 조건을 만족하는 트리 수만큼 더해줌(n-r개)</li>
<li>그 후 현재 l이 max값과 같다면 l을 늘렸을 때 max인 값이 하나 줄어드는 것이니 maxCount 를 -1 해준 뒤에 l 값을 +1 해 줌</li>
</ul>
</li>
<li><p>모든 반복문 종료 후 count return으로 종료</p>
</li>
</ul>
<p>다른 방버브로도 풀 수 있을것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[2537. Count the Number of Good Subarrays]]></title>
            <link>https://velog.io/@tech_n0/2537.-Count-the-Number-of-Good-Subarrays</link>
            <guid>https://velog.io/@tech_n0/2537.-Count-the-Number-of-Good-Subarrays</guid>
            <pubDate>Tue, 29 Apr 2025 07:01:19 GMT</pubDate>
            <description><![CDATA[<h1 id="문제">문제</h1>
<ul>
<li>링크: <a href="https://leetcode.com/problems/count-the-number-of-good-subarrays/description/">https://leetcode.com/problems/count-the-number-of-good-subarrays/description/</a></li>
</ul>
<p>모든 i~j</p>
<p>범위에 대해</p>
<p>등장 숫자 세고</p>
<p>쌍(pair) 수 세서k개 이상이면 count 증가</p>
<pre><code class="language-python">class Solution:
    def countGood(self, nums: List[int], k: int) -&gt; int:
###         n = len(nums)
        count = 0
        for i in range(n):
            fr = {}
            pair = 0
            for j in range(i, n):
                num = nums[j]
                if num in fr:
                    pair += fr[num]
                    fr[num] += 1
                else:
                    fr[num] = 1
                if pair &gt;= k:
                    count += 1
        return count
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[1534. Count Good Triplets]]></title>
            <link>https://velog.io/@tech_n0/1534.-Count-Good-Triplets</link>
            <guid>https://velog.io/@tech_n0/1534.-Count-Good-Triplets</guid>
            <pubDate>Tue, 29 Apr 2025 06:59:29 GMT</pubDate>
            <description><![CDATA[<h1 id="문제">문제</h1>
<p><a href="https://leetcode.com/problems/count-good-triplets/?envType=daily-question&amp;envId=2025-04-14">https://leetcode.com/problems/count-good-triplets/?envType=daily-question&amp;envId=2025-04-14</a></p>
<ul>
<li><p>arr 와 abc 가 주어짐</p>
</li>
<li><p>arr의 숫자중 3개(i, j, k)를  뽑아</p>
<ul>
<li><p><code>0 &lt;= i &lt; j &lt; k &lt; arr.length</code></p>
</li>
<li><p><code>|arr[i] - arr[j]| &lt;= a</code></p>
</li>
<li><p><code>|arr[j] - arr[k]| &lt;= b</code></p>
</li>
<li><p><code>|arr[i] - arr[k]| &lt;= c</code></p>
<p>를 만족하는 조합의 갯수를 return하는 문제</p>
</li>
</ul>
</li>
</ul>
<hr>
<p>i, j, k 니까 3중 for문을 쓰면 될듯 근데 타임오바 안나려나</p>
<h1 id="코드">코드</h1>
<pre><code class="language-python">class Solution(object):
    def countGoodTriplets(self, arr, a, b, c):
        count = 0
        n = len(arr)
        for i in range(n):
            for j in range(i + 1, n):
                if abs(arr[i] - arr[j]) &lt;= a:
                    for k in range(j + 1, n):
                        if abs(arr[j] - arr[k]) &lt;= b and abs(arr[i] - arr[k]) &lt;= c:
                            count += 1
        return count
</code></pre>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/3719fe3d-1a7a-4bea-86f8-f93c252e5706/image.png" alt=""></p>
<p>억지 함수화</p>
<pre><code class="language-python">class Solution(object):
    def countGoodTriplets(self, arr, a, b, c):
        def check_good(arr, x, y, z):
            if abs(arr[x] - arr[y]) &lt;= z:
                return True
            else:
                return False
        # ----------------------------------------- 
        count = 0
        arr_len = len(arr)

        for i in range(arr_len):
            # print(i)
            for j in range(i + 1, arr_len):
                # print(&#39;-&#39;, j)
                if check_good(arr, i, j, a) == True and i &lt; j:
                    for k in range(j + 1, arr_len):
                        # print(arr[i], arr[j], arr[k])
                        if check_good(arr, j, k, b) == True and check_good(arr, i, k, c) == True and j &lt; k:
                            count += 1
        return count
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[2843. Count Symmetric Integers]]></title>
            <link>https://velog.io/@tech_n0/2843.-Count-Symmetric-Integers</link>
            <guid>https://velog.io/@tech_n0/2843.-Count-Symmetric-Integers</guid>
            <pubDate>Tue, 29 Apr 2025 06:57:53 GMT</pubDate>
            <description><![CDATA[<h1 id="문제">문제</h1>
<ul>
<li><a href="https://leetcode.com/problems/count-symmetric-integers/description/?envType=daily-question&amp;envId=2025-04-11">https://leetcode.com/problems/count-symmetric-integers/description/?envType=daily-question&amp;envId=2025-04-11</a></li>
</ul>
<h1 id="코드">코드</h1>
<pre><code class="language-python">class Solution(object):
    def countSymmetricIntegers(self, low, high):
        # low 가 1의 자리 수라면 짝수 자리 수부터 시작해도 됨(홀수는 무조건 비대칭 이니까)
        if len(str(low)) % 2 != 0:
            start = 10 ** len(str(low))
        else:
            start = low

        count = 0
        # 나누기로 해결할수 있을것 같은데 홀수 자릿수 일 때 넘기는것도
        for i in range(start,high+1):
            numlen = len(str(i))
            if numlen % 2 != 0:
                continue
            elif numlen == 2:
                n1 = i%10
                n2 = i/10
                if n1 == n2:
                    count += 1
            elif numlen == 4:
                n1 = i%10
                n2 = (i%100)/10
                n3 = (i%1000)/100
                n4 = i/1000
                if n1+n2 == n3+ n4:
                    count += 1
        return count

</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[LeetCode - 3396. Minimum Number of Operations to Make Elements in Array Distinct]]></title>
            <link>https://velog.io/@tech_n0/LeetCode-3396.-Minimum-Number-of-Operations-to-Make-Elements-in-Array-Distinct</link>
            <guid>https://velog.io/@tech_n0/LeetCode-3396.-Minimum-Number-of-Operations-to-Make-Elements-in-Array-Distinct</guid>
            <pubDate>Tue, 08 Apr 2025 13:05:48 GMT</pubDate>
            <description><![CDATA[<h1 id="문제">문제</h1>
<ul>
<li><p>링크: <a href="https://leetcode.com/problems/minimum-number-of-operations-to-make-elements-in-array-distinct/description/?envType=daily-question&amp;envId=2025-04-08">https://leetcode.com/problems/minimum-number-of-operations-to-make-elements-in-array-distinct/description/?envType=daily-question&amp;envId=2025-04-08</a></p>
</li>
<li><p>정수 배열 <code>nums</code> 이 주어짐(<code>list[int]</code>)</p>
</li>
<li><p>배열의 각 원소가 고유 값이 될 때 까지</p>
</li>
<li><p>배열 맨 앞의 3개의 원소를 제거하는 작업을 수행함</p>
</li>
<li><p>종료 조건 불 만족 이면서 원소의 개수가 3개 미만이면 나머지 원소를 모두 버림</p>
</li>
<li><p>모든 원소가 고유 값을 가지게 하는 최소 버림 연산 횟수를 반환하는 문제</p>
</li>
</ul>
<hr>
<h1 id="풀이">풀이</h1>
<ul>
<li><p>모든 원소가 고유 값인지 확인하는 함수 만들기</p>
<ul>
<li>모두 고유 값이면 참 반환</li>
</ul>
</li>
<li><p>맨 앞 3개의 원소를 제거</p>
</li>
<li><p>모든 원소가 고유 값이 아니면서 남은 원소 개수가 3 미만일 경우 모두 버림 처리하는 조건문 필요</p>
</li>
<li><p>몇번 반복이 아닌 유니크 해질 때 까지 반복임으로 while 문으로 반복하는 것이 가장 적합할 것</p>
</li>
</ul>
<pre><code class="language-python">class Solution(object):
    def minimumOperations(self, nums):
        def is_unique(arr):
            return len(arr) == len(set(arr))

        count = 0

        while len(nums) &gt; 0:
            if is_unique(nums):
                return count
            if len(nums) &lt; 3:
                nums = []
            else:
                nums = nums[3:]
            count += 1

        return count
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[3. 회귀 알고리즘과 모델 규제]]></title>
            <link>https://velog.io/@tech_n0/3.-%ED%9A%8C%EA%B7%80-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98%EA%B3%BC-%EB%AA%A8%EB%8D%B8-%EA%B7%9C%EC%A0%9C</link>
            <guid>https://velog.io/@tech_n0/3.-%ED%9A%8C%EA%B7%80-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98%EA%B3%BC-%EB%AA%A8%EB%8D%B8-%EA%B7%9C%EC%A0%9C</guid>
            <pubDate>Sat, 05 Apr 2025 04:21:26 GMT</pubDate>
            <description><![CDATA[<h1 id="3-1-k-최근접-이웃-회귀">3-1 k-최근접 이웃 회귀</h1>
<h3 id="목표">목표</h3>
<ul>
<li>지도 학습의 한 종류인 회귀문제 이해</li>
<li>k-최근접 이웃 알고리즘 사용 농어 무게 예측하는 회귀 문제 풀기</li>
</ul>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/cf0d2a7c-9149-4a1e-bea8-dd02a98ba7e7/image.png" alt=""></p>
<pre><code>회귀하면 또 니체죠…</code></pre><hr>
<h2 id="k-최근접-이웃-회귀">k-최근접 이웃 회귀</h2>
<ul>
<li>지도학습은 크게 분류와 <strong>회귀(regression)로 나뉨</strong></li>
<li>전 장(1-2장)에서 한게 분류</li>
<li>회귀 - 클래스 중 하나로 분류하는 것이 아닌 임의 어떤 숫자를 예측하는 문제<ul>
<li>ex). 내년 경제 성장률 예측, 배달 도착 시간 예측</li>
<li>ch.3 에선 농어의 무게를 예측하는 회귀</li>
</ul>
</li>
</ul>
<aside>
💡

<p>근데 회귀는 돌아온다는 뜻 아님?</p>
</aside>

<p><img src="https://velog.velcdn.com/images/tech_n0/post/d7cb35bf-d285-4b9d-a375-5f32d7630c93/image.png" alt=""></p>
<pre><code>회귀는 돌아오는 거야!</code></pre><p>‘프랜시스 콜턴’이라는 통계 / 사회 학자가 키 큰 부모의 아이가 부모보다 더 크지 않다는 사실을 관찰하고 이를</p>
<p>평균으로 회귀한다</p>
<p>라고 표현했답니다. 그 후로 이렇게 굳어졌다는…</p>
<p>그래서 정의하면</p>
<h3 id="회귀-두-변수-사이의-상관관계를-분석하는-방법">회귀: 두 변수 사이의 상관관계를 분석하는 방법</h3>
<p>이제 k-최근접 이웃 알고리즘이 분류와 회귀에 적용되는 방식을 알아보자</p>
<hr>
<h3 id="k-최근접-이웃-분류-알고리즘">k-최근접 이웃 분류 알고리즘</h3>
<ol>
<li>예측하려는 샘플에 가장 가까운 샘플 k개 선택</li>
<li>샘플들의 클래스 확인해 다수 클래스를 새로운 샘플의 클래스로 예측</li>
</ol>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/c05cd8da-df9d-40df-a033-9c51e4f4184b/image.png" alt=""></p>
<ul>
<li>k = 3 (샘플 3개 일 때)</li>
<li>x 의 이웃에는 원1개 사각형2개 임으로 x는 네모가 된다.</li>
</ul>
<h3 id="k-최근접-이웃-회귀-1">k-최근접 이웃 회귀</h3>
<ol>
<li>예측할 샘플에 가장 가까운 샘플 개 선택<ul>
<li>but, 회귀니까 이웃한 샘플의 타깃은 클래스가 아닌 임의의 수치(값)</li>
</ul>
</li>
<li>이웃 샘플의 수치의 평균을 구하면 샘플 x의 타깃 예측 가능</li>
</ol>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/b68ef4a2-4769-4a31-9efa-3b18c360bdb8/image.png" alt=""></p>
<p>긑</p>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/cdc10797-8f22-406b-aeea-cc379c3386d7/image.png" alt=""></p>
<hr>
<h2 id="데이터준비">데이터준비</h2>
<ul>
<li>농어 길이가 특성이고 무게가 타깃인 넘파이 배열을 준비하자</li>
</ul>
<pre><code class="language-python">import numpy as np

perch_length = np.array(
    [8.4, 13.7, 15.0, 16.2, 17.4, 18.0, 18.7, 19.0, 19.6, 20.0,
     21.0, 21.0, 21.0, 21.3, 22.0, 22.0, 22.0, 22.0, 22.0, 22.5,
     22.5, 22.7, 23.0, 23.5, 24.0, 24.0, 24.6, 25.0, 25.6, 26.5,
     27.3, 27.5, 27.5, 27.5, 28.0, 28.7, 30.0, 32.8, 34.5, 35.0,
     36.5, 36.0, 37.0, 37.0, 39.0, 39.0, 39.0, 40.0, 40.0, 40.0,
     40.0, 42.0, 43.0, 43.0, 43.5, 44.0]
     )
perch_weight = np.array(
    [5.9, 32.0, 40.0, 51.5, 70.0, 100.0, 78.0, 80.0, 85.0, 85.0,
     110.0, 115.0, 125.0, 130.0, 120.0, 120.0, 130.0, 135.0, 110.0,
     130.0, 150.0, 145.0, 150.0, 170.0, 225.0, 145.0, 188.0, 180.0,
     197.0, 218.0, 300.0, 260.0, 265.0, 250.0, 250.0, 300.0, 320.0,
     514.0, 556.0, 840.0, 685.0, 700.0, 700.0, 690.0, 900.0, 650.0,
     820.0, 850.0, 900.0, 1015.0, 820.0, 1100.0, 1000.0, 1100.0,
     1000.0, 1000.0]
     )</code></pre>
<ul>
<li>위 데이터의 형태를 확인하기 위해 산점도를 그릴건데<ul>
<li>특성이 길이 하나 이기 때문에 특성 데이터(길이)를 x축, 타깃 데이터를 y축으로 잡는다.</li>
</ul>
</li>
</ul>
<pre><code class="language-python">import matplotlib.pyplot as plt

plt.scatter(perch_length, perch_weight)
plt.xlabel(&#39;length&#39;)
plt.ylabel(&#39;weight&#39;)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/1648843f-9204-4346-bd10-6fb191b52da1/image.png" alt=""></p>
<ul>
<li><p>농어 길이가 커지면 무게도 늘어나는 관계를 찾을 수 있다.</p>
</li>
<li><p>이제 데이터를 훈련 세트&amp;테스트 세트로 나누자</p>
</li>
</ul>
<pre><code class="language-python">from sklearn.model_selection import train_test_split

train_input, test_input, train_target, test_target = train_test_split(
    perch_length, perch_weight, random_state=42)</code></pre>
<ul>
<li><p>train_test_split 함수로 훈련/테스트 세트를 나눴다</p>
</li>
<li><p>결과 유지를 위해 시드는 역시 42로 고정</p>
</li>
<li><p>사이킷런에서 훈련 세트는 2차원 배열이여야 함</p>
</li>
</ul>
<p><code>[1, 2, 3]</code>  → <code>[[1], [2], [3]]</code> </p>
<p>크기: (3, )         |          (3, 1)</p>
<p>이렇게</p>
<aside>
💡

<p>하나하나 변환하기 귀찮아하는 게으른 나를 위한 딸깍이 있다.</p>
<p>그것이 파이썬 쓰는 이유니까…</p>
</aside>

<h3 id="reshape">reshape</h3>
<ul>
<li>넘파이 배열 크기를 바꾸는 매서드</li>
<li>ex). (4, ) 배열을 (2, 2)크기로 바꾸기</li>
</ul>
<pre><code class="language-python">test_array = np.array([1,2,3,4])
print(test_array.shape)

test_array = test_array.reshape(2,2)
print(test_array.shape)

# 결과
(4,)
(2, 2)</code></pre>
<ul>
<li>이렇게 배열의 크기를 바꿀 수 있다.</li>
</ul>
<p>!주의</p>
<ul>
<li>reshape() 메서드는 크기가 바뀐 새로운 배열을 반환할 때 지정한 크기가 원본 배열에 있는 원소의 개수와 다르면 에러 발생</li>
</ul>
<pre><code class="language-python">test_array = test_array.reshape(2, 3)

# 결과
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
&lt;ipython-input-9-4d70e0624945&gt; in &lt;cell line: 0&gt;()
----&gt; 1 test_array = test_array.reshape(2, 3)

ValueError: cannot reshape array of size 4 into shape (2,3)</code></pre>
<p>이제 reshape를 활용하여  <code>train_input</code> 과 <code>test_input</code> 을 2차원 배열로 만들자</p>
<ul>
<li>넘파이는 배열의 크기를 자동으로 지정하는 기능도 제공한다</li>
<li>reshape 매서드 에서 크기에 -1 을 지정하면 나머지 원소 개수로 모두 채우라는 의미</li>
<li>ex). 첫 번째 크기를 나머지 원소 개수로 채우고 두번 째 크기를 1로 하기</li>
</ul>
<pre><code class="language-python">train_input = train_input.reshape(-1, 1)
test_input = test_input.reshape(-1, 1)
print(train_input.shape, test_input.shape)

#결과
(42, 1) (14, 1)</code></pre>
<p>이제 준비한 훈련 세트로 k-최근접 이웃 알고리즘을 훈련시켜보자</p>
<hr>
<h2 id="결졍계수r2">결졍계수($R^2$)</h2>
<h3 id="kneighborsregressor">KNeighborsRegressor</h3>
<ul>
<li>사이킷런에서 k-최근접 이웃 회기 알고리즘을 구현한 클래스</li>
<li>객체를 생성하고 fit으로 회귀 모델을 훈련하자</li>
</ul>
<pre><code class="language-python">from sklearn.neighbors import KNeighborsRegressor

knr = KNeighborsRegressor()
knr.fit(train_input, train_target)

print(knr.score(test_input, test_target))

# 결과
**0.992809406101064**</code></pre>
<h3 id="점수의-의미">점수의 의미?</h3>
<ul>
<li><p>분류의 경우 테스트 세트에 있는 샘플을 정확하게 분류한 개수의 비율(정확도, 정답 맞힌 개수 비율)</p>
</li>
<li><p>회귀에서는 정확한 숫자를 맞히는 것은 거의 불가능</p>
</li>
<li><p>회귀에서는 다르게 평가하는데 이 점수를 결정 계수라고 함($R^2$)</p>
</li>
</ul>
<h3 id="r2--1----frac타깃---예측2의-합타깃---평균2-의-합">$R^2 = 1 -  \frac{(타깃 - 예측)^2의 합}{(타깃 - 평균)^2 의 합}$</h3>
<ul>
<li>위에서는 0.99 정도로 좋은 값이 나왔다.</li>
<li>하지만 정확도처럼 $R^2$가 얼마나 좋은지 이해하기 어렵다.</li>
<li>타깃과 예측값 사이의 차이를 구해보면 어느정도 예측이 벗어났는지 가늠하기 좋음</li>
</ul>
<h3 id="mean_absolute_error">mean_absolute_error</h3>
<ul>
<li>타깃과 예측의 절대값 오차를 평균하여 반환(사이킷런 제공 도구)</li>
</ul>
<pre><code class="language-python">from sklearn.metrics import mean_absolute_error

# 테스트 세트 대한 예측 생성
test_prediction = knr.predict(test_input)

# 테스트 세트에 대한 평균 절대값 오차 계산
mae = mean_absolute_error(test_target, test_prediction)
print(mae)

# 결과
19.157142857142862</code></pre>
<ul>
<li><p>예측이 평균적으로 19g정도 타깃값과 다르다.</p>
</li>
<li><p>위 결과는 훈련 세트로 훈련하고 테스트 세트로 평가했다.</p>
</li>
<li><p>평가도 훈련세트로 하면 어떻게 될까?</p>
</li>
<li><p>훈련 세트의 $R^2$ 점수를 확인하자</p>
</li>
</ul>
<pre><code class="language-python">print(knr.score(train_input, train_target))

# 결과
0.9698823289099254</code></pre>
<ul>
<li><p>보통 훈련 세트의 점수가 조금 더 높게 나온다(당연히 이걸로 훈련했으니까)</p>
</li>
<li><p>과대적합: 훈련 세트에서 점수가 좋은데 테스트 세트에서 점수가 나쁜 경우</p>
</li>
<li><p>과소적합: 훈련 세트보다 테스트 세트가 점수가 높거나 두 점수가 모두 너무 낮은 경우</p>
<p>  위 경우는 테스트 세트 점수가 높으니 과소적합</p>
</li>
</ul>
<p>모델을 좀 더 복잡하게 만들면 이 문제를 해결할 수 있다.</p>
<ul>
<li>k-최근접 이웃 알고리즘에서 모델을 복잡하게 만드는 방법<ul>
<li>이웃의 개수 k 를 줄인다.</li>
<li>이웃을 줄이면 훈련세트에 있는 국지적인 패턴에 민감해짐(비교하는 범위가 줄어드니까)</li>
<li>늘리면 데이터 전반의 일반적 패턴을 따를 것임</li>
</ul>
</li>
<li>이웃 개수를 5에서 3으로 줄여보자</li>
</ul>
<pre><code class="language-python">knr.n_neighbors = 3

knr.fit(train_input, train_target)
print(knr.score(train_input, train_target))

print(knr.score(test_input, test_target))

# 결과
0.9804899950518966
0.9746459963987609</code></pre>
<p>k값을 줄이니 훈련 세트 $R^2$ 점수가 높아졌고</p>
<p>테스트 세트의 점수가 낮아져 과소적합 문제를 해결했다.</p>
<ul>
<li>만약 과대적합이였다면?</li>
<li>모델을 덜 복잡하게 만들면 됨</li>
<li>k값을 늘리라는 소리</li>
</ul>
<h3 id="키워드-정리">키워드 정리</h3>
<ul>
<li>회귀: 임의의 수치를 예측하는 문제. 타깃값도 임의의 수치가 됨</li>
<li>k-최근접 이웃 회귀: k-최근접 이웃 알고리즘을 사용해 문제 품<ul>
<li>가장 가까운 이웃 샘플 찾고 이 샘플들의 타깃값을 평균으로 예측</li>
</ul>
</li>
<li>결졍계수($R^2$): 대표적 회귀문제 성능 측정 도구<ul>
<li>1에 가까울수록 좋고, 0에 가까우면 성능이 안좋다.</li>
</ul>
</li>
<li>과대적합: 모델의 훈련 세트 성능이 테스트 세트 성능보다 훨씬 높을 때 일어남<ul>
<li>모델이 훈련 세트에 너무 집착해 데이터에 내제된 거시적 패턴 감지 불가</li>
</ul>
</li>
<li>과소적합: 훈련세트와 테스트 세트 성느 모두 낮거나 테스트 세트 성능이 높을 때 발생<ul>
<li>더 복잡한 모델 사용해 훈련세트에 잘 맞는 모델 만들어서 해결</li>
</ul>
</li>
</ul>
<hr>
<h1 id="3-2-선형-회귀">3-2 선형 회귀</h1>
<ul>
<li>책의 상황<ul>
<li>이상하게 큰 농어를 골라 무게를 예측하니 실제 무계와 너무 차이가 큰 문제 발생</li>
</ul>
</li>
</ul>
<h2 id="k-최근접-이웃의-한계">k-최근접 이웃의 한계</h2>
<ul>
<li>우선 전에 쓴 데이터와 모델을 준비하자</li>
</ul>
<pre><code class="language-python">import numpy as np

perch_length = np.array(
    [8.4, 13.7, 15.0, 16.2, 17.4, 18.0, 18.7, 19.0, 19.6, 20.0,
     21.0, 21.0, 21.0, 21.3, 22.0, 22.0, 22.0, 22.0, 22.0, 22.5,
     22.5, 22.7, 23.0, 23.5, 24.0, 24.0, 24.6, 25.0, 25.6, 26.5,
     27.3, 27.5, 27.5, 27.5, 28.0, 28.7, 30.0, 32.8, 34.5, 35.0,
     36.5, 36.0, 37.0, 37.0, 39.0, 39.0, 39.0, 40.0, 40.0, 40.0,
     40.0, 42.0, 43.0, 43.0, 43.5, 44.0]
     )
perch_weight = np.array(
    [5.9, 32.0, 40.0, 51.5, 70.0, 100.0, 78.0, 80.0, 85.0, 85.0,
     110.0, 115.0, 125.0, 130.0, 120.0, 120.0, 130.0, 135.0, 110.0,
     130.0, 150.0, 145.0, 150.0, 170.0, 225.0, 145.0, 188.0, 180.0,
     197.0, 218.0, 300.0, 260.0, 265.0, 250.0, 250.0, 300.0, 320.0,
     514.0, 556.0, 840.0, 685.0, 700.0, 700.0, 690.0, 900.0, 650.0,
     820.0, 850.0, 900.0, 1015.0, 820.0, 1100.0, 1000.0, 1100.0,
     1000.0, 1000.0]
     )</code></pre>
<ul>
<li>데이터를 훈련 세트와 테스트 세트로 나누자. 특성 데이터는 2차원 배열로 변환한다.</li>
</ul>
<pre><code class="language-python">from sklearn.model_selection import train_test_split

train_input, test_input, train_target, test_target
= train_test_split(perch_length, perch_weight, random_state = 42)

train_input = train_input.reshape(-1, 1)
test_input = test_input.reshape(-1, 1)</code></pre>
<ul>
<li>이웃을 3으로하는 모델을 훈련하고 길이가 50인 농어 무게를 예측해 보자</li>
</ul>
<pre><code class="language-python">from sklearn.neighbors import KNeighborsRegressor

knr = KNeighborsRegressor(n_neighbors = 3)
knr.fit(train_input, train_target)

print(knr.predict([[50]]))

# 결과
[1033.33333333]</code></pre>
<ul>
<li>이 길이 50cm의 농어는 1.5kg인데 예측은 더 작은 무계로 나왔다.</li>
</ul>
<p>어째서…?</p>
<ul>
<li>그 이유를 확인하기 위해 예측에 사용된 최근접 이웃을 산점도 그래프로 시각화 해보자<ul>
<li>아마 너무 길이가 길어 더 큰 개체가 표본에 없어 이웃이 오른쪽으로 치우쳐서 그러지 않을까?</li>
</ul>
</li>
</ul>
<pre><code class="language-python">import matplotlib.pyplot as plt

distances, indexes = knr.kneighbors([[50]])

plt.scatter(train_input, train_target)

# 이웃 샘플 강조
plt.scatter(train_input[indexes], train_target[indexes], marker = &#39;D&#39;)

# 길이 50 농어 데이터 강조
plt.scatter(50, 1033, marker = &#39;D&#39;)
plt.xlabel(&#39;length&#39;)
plt.ylabel(&#39;weight&#39;)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/28012878-fc37-476d-b31e-3d8e9c713893/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/9bedf2a5-3a6d-40e5-b8bd-b48f0810f3d9/image.png" alt=""></p>
<p>빙고</p>
<ul>
<li>당연히 예측하려는 값이 혼자 끝에 있는데 평균 값이 이렇게 나올 수 밖에 없지…<ul>
<li>범위를 벗어나는 값(여기선 대략 길이가 50 넘어가는 값)을 아무라 계산해도 <code>1033.33333333</code> 만 나오게 된다.</li>
</ul>
</li>
<li>개인적인 생각이지만 길이 대비 무게 값 관계 평균을 계산해서 점화식을 만들어 적용하는 것이 더 좋을 것 같다.</li>
</ul>
<p>이 문제를 해결하는 다른 알고리즘이 있답니다.</p>
<hr>
<h2 id="선형-회귀">선형 회귀</h2>
<ul>
<li>특성이 하나인 경우 어떤 직선을 학습하는 알고리즘</li>
<li>간단하고 성능이 뛰어남(비교적)</li>
</ul>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/8fad142c-9d64-4efa-81f9-b5570135fe9d/image.png" alt=""></p>
<ul>
<li>세 직선 중 뭐가 농어 데이터를 가장 잘 표현하는가?</li>
<li>딱 봐도 3번이다.<ul>
<li>1번 직선은 모든 농어를 하나의 무계로</li>
<li>2번은 원래와 반대로</li>
<li>3번이 가장 그럴싸함</li>
</ul>
</li>
<li>근데 컴퓨터는 이 ‘딱 봐도’ 를 못한다. 왜? 인간이 아닌 깡통 이니까…</li>
</ul>
<aside>
💡

<p>그럼 이 직선을 인식하는 것도 구현해야 함?</p>
</aside>

<ul>
<li>그러기엔 난 너무 게으른 사람이다.</li>
</ul>
<h3 id="linearregression">LinearRegression</h3>
<ul>
<li><p>선형 회귀 알고리즘을 구현한 사이킷런 제공 클래스</p>
</li>
<li><p>한마디로 ‘딱 봐도 이거야!’ 하는 직선을 이 깡통이 찾을 수 있게 해주는 클래스</p>
</li>
<li><p>마찬가지로 fit(훈련), score(평가), predict(예측) 메서드 제공</p>
</li>
<li><p>훈련 모델 만들고 예측해보자</p>
</li>
</ul>
<pre><code class="language-python">from sklearn.linear_model import LinearRegression
lr = LinearRegression()

lr.fit(train_input, train_target)

print(lr.predict([[50]]))

# 결과
[1241.83860323]</code></pre>
<ul>
<li>k-최근접 이웃 회귀 보다 다 더 근접하게 예측했다.</li>
</ul>
<aside>
💡

<p>LinearRegression은 어떻게 선을 그려서 예측할까?</p>
</aside>

<ul>
<li>다들 젊은 시절에 배웠던  직선 그리기 조건을 떠올려 보자</li>
<li>기울기와 절편이 주어져야 한다.</li>
</ul>
<p>$y = a \times x + b$</p>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/c37bfb3b-81a0-40f7-810e-73f32e40621a/image.png" alt=""></p>
<p>x: 농어 길이</p>
<p>y: 농어 무게</p>
<ul>
<li>x, y는 우리가 주어줬는데 a,b는??</li>
<li>LinearRegression 클래스가 기울기와 절편을 스스로 잘 찾아서 객체의 <code>coef_</code> 와 <code>intercept_</code> 에 저장한다.</li>
<li>출력 해보자</li>
</ul>
<pre><code class="language-python">print(lr.coef_, lr.intercept_)

# 결과
[39.01714496] -709.0186449535477</code></pre>
<p>*머신러닝에선 기울기를 coefficient(계수) 또는 weight(가중치) 라고 하기도 함</p>
<ul>
<li><code>lr.coef_</code>, <code>lr.intercept_</code> 를 머신러닝 알고리즘이 찾은 값 이라는 의마로 <strong>모델 파라미터</strong> 라고 부름</li>
<li><strong>모델 기반 학습</strong>: 최적의 모델 피라미터를 찾는 것</li>
<li><strong>사례 기반 학습</strong>: 모델 파라미터 없이 훈련 세트를 저장 하는 게 훈련의 전부인 학습(k-최근접 이웃)</li>
</ul>
<hr>
<p>기울기와 절편이 있으니 직선을 그려보자</p>
<ul>
<li>15에서 50까지 1차 방정식 그래프(직선)를 그릴거다.</li>
<li>기울기가  39, 절편이 -709이니 이걸 식에 대입만 해주면 된다</li>
<li>15<em>39-709, 50</em>39-709</li>
</ul>
<pre><code class="language-python">plt.scatter(train_input, train_target)

plt.plot([15, 50], [15*lr.coef_+lr.intercept_, 50*lr.coef_+lr.intercept_])

plt.scatter(50, 1241.8, marker = &#39;^&#39;)
plt.xlabel(&#39;length&#39;)
plt.ylabel(&#39;weight&#39;)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/c1902fb3-a15b-467d-8f81-3a6822fb624c/image.png" alt=""></p>
<ul>
<li>이 직선이 바로 선형 회귀 알고리즘이 데이터 셋에서 찾은 최적의 직선</li>
</ul>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/c5c9e5c8-e171-4eb8-8b2d-bd1196a72b57/image.png" alt=""></p>
<p>성공</p>
<ul>
<li>이제 훈련세트 범위 밖의 물고기도 예측가능하다.</li>
<li>$R^2$점수도 확인해 보자</li>
</ul>
<pre><code class="language-python">print(lr.score(train_input, train_target))
print(lr.score(test_input, test_target))

# 결과
0.939846333997604
0.8247503123313558</code></pre>
<hr>
<h2 id="다항-회귀">다항 회귀</h2>
<ul>
<li><p>근데 직선 그래프를 좀 더 자세히 들여다 보자</p>
<p>  <img src="https://velog.velcdn.com/images/tech_n0/post/0ad1321f-7bfb-4e6a-a9b8-a59670bff747/image.png" alt=""></p>
</li>
</ul>
<ul>
<li><p>이질적인 느낌이 들지 않는가?</p>
</li>
<li><p>직선이 우상향 이다.</p>
</li>
<li><p>이 직선대로 예측하면 농어의 무게가 0 이하로 내려가게 된다.</p>
</li>
<li><p>0g인 반물질 물고기가 현실에 존재 할 리 없는데 말이다.</p>
</li>
<li><p>최선의 직선 말고 곡선을 찾으면 더 좋을 것 같다.</p>
</li>
<li><p>곡선이 있는 그래프는 2차 방정식이 필요하겠지?</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/a405f490-bd15-4a9e-b385-b3dff5257e3b/image.png" alt=""></p>
<ul>
<li><p>그럼 2차 방정식에 맞게 길이를 또 제곱 해줘야 함</p>
</li>
<li><p>2장에서 썼던 column_stack 함수를 쓰면 간단하게 할수있다.</p>
</li>
<li><p>농어의 길이를 제곱해서 원래 데이터 앞에 붙여보자</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/8540e72f-5b46-46b3-a0a0-c5252e4e579a/image.png" alt=""></p>
<pre><code class="language-python">train_poly = np.column_stack((train_input ** 2, train_input))
test_poly = np.column_stack((test_input ** 2, test_input))

print(train_poly.shape, test_poly.shape)

# 결과
(42, 2) (14, 2)</code></pre>
<ul>
<li>이제 선형 회귀 모델을 다시 훈련 시킬 것인데</li>
<li>2차 방정식 그래프를 찾기 위해 제곱 항을 추가했지만 타깃 값은 그대로 사용한다.</li>
<li>목표 값 은 어떤 그래프를 훈련하던 바꿀 필요가 없다는 사실을 알아두자</li>
</ul>
<pre><code class="language-python">lr = LinearRegression()
lr.fit(train_poly, train_target)

print(lr.predict([[50**2, 50]]))

# 결과
[1573.98423528]</code></pre>
<ul>
<li><p>좀 더 정답에 가까운 예측 결과다 나왔다.</p>
</li>
<li><p>이 모델이 훈련한 계수와 절편을 출력해 보자</p>
</li>
</ul>
<pre><code class="language-python">print(lr.coef_, lr.intercept_)

# 결과
[  1.01433211 -21.55792498] 116.0502107827827</code></pre>
<ul>
<li>이 식을 2차방정식 그래프 식에 대입하면 모델이 학습한 그래프의 방정식을 알 수 있다.</li>
</ul>
<p>$무계 = 1.01 \times 길이^2 - 21.6 \times 길이 + 116.05$</p>
<ul>
<li>이러한 방정식을 <strong>다항식 이라 부르고</strong></li>
<li>다항식을 사용한 선형 회귀를 <strong>다항 회귀</strong>라고 함\</li>
</ul>
<hr>
<p>이제 산점도에 그래프를 그려보자</p>
<pre><code class="language-python">point = np.arange(15, 50)

#산점도
plt.scatter(train_input, train_target)
# 2차 방정식 그래프
plt.plot(point, 1.01*point**2 - 21.6*point + 116.05)

# 50 농어 데이터
plt.scatter([50], [1574], marker = &#39;^&#39;)
plt.xlabel(&#39;length&#39;)
plt.ylabel(&#39;weight&#39;)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/01934b2c-19ab-4f15-9266-b97f005a021f/image.png" alt=""></p>
<ul>
<li><p>단순 선형 회귀보다 다항 회귀가 더 좋은 그래프가 그려졌다.</p>
</li>
<li><p>역시나 훈련 세트와 테스트세트의 R스퀘어 점수를 살펴보자</p>
</li>
</ul>
<pre><code class="language-python">print(lr.score(train_poly, train_target))
print(lr.score(test_poly, test_target))

# 결과
0.9706807451768623
0.9775935108325122</code></pre>
<ul>
<li>정말 비슷하지만 테스트 점수가 쪼끔 더 높다…</li>
<li>아직 과소적합이 남아있다는 뜻</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 - 유연근무제]]></title>
            <link>https://velog.io/@tech_n0/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%9C%A0%EC%97%B0%EA%B7%BC%EB%AC%B4%EC%A0%9C</link>
            <guid>https://velog.io/@tech_n0/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%9C%A0%EC%97%B0%EA%B7%BC%EB%AC%B4%EC%A0%9C</guid>
            <pubDate>Tue, 25 Mar 2025 06:53:39 GMT</pubDate>
            <description><![CDATA[<h1 id="문제">문제</h1>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/388351">https://school.programmers.co.kr/learn/courses/30/lessons/388351</a></p>
<hr>
<h1 id="풀이">풀이</h1>
<ul>
<li><p>직원 n명의 희망 출근 시간이 담긴 <code>schedules</code></p>
</li>
<li><p>직원들의 일주일 출근 시간 내역 2차원 배열 <code>timelogs</code></p>
</li>
<li><p>시작한 요일 <code>startday</code> (월, 화, 수, 목, 금, 토, 일 == 1, 2, 3, 4, 5, 6, 7)</p>
</li>
<li><p>유저 수 n 만큼 반복해야 함</p>
</li>
<li><p>직원 별로 타임 로그를 순회 해야 함</p>
<ul>
<li>지각 발생 시 탈출 해야 효율 올라감</li>
</ul>
</li>
<li><p>주말에는 지각 안잡으니 주말은 봐줘야 함</p>
</li>
<li><p>현재 요일을 체크해서 7(일요일)이라면 다음날에는 1(월요일) 부터 시작되게 조건문 줘야함</p>
</li>
<li><p>순회를 끝냈을 때 지각이 없었다면 count + 1</p>
</li>
<li><p>유저별 순회 종료 후 count 값 리턴</p>
</li>
</ul>
<h1 id="35점-코드">35점 코드</h1>
<pre><code class="language-python">def solution(schedules, timelogs, startday):
    n = len(schedules)
    cont = 0

    for i in range(n):
        # 현재 직원 출근 희망 시간
        WSTime = schedules[i]
        # 출근 인정 시간(+10)
        PSTime = WSTime + 10
        # 성공실패
        SS = True

        day = startday
        for j in timelogs[i]:
            # 평일일때만 진행
            if 1 &lt;= day &lt;= 5:
                if j &gt;PSTime:ㅁ
                    SS = False
                    break  
            # 일주일 지나갔나 체크
            if day == 7:
                day = 1
            else:
                day += 1

        if SS:
            cont += 1

    return cont

# 시각 변환식: h시 m분 = h * 100 + m </code></pre>
<h1 id="통과한-코드">통과한 코드</h1>
<pre><code class="language-python">def solution(schedules, timelogs, startday):
    def convert_time(time):
        h, m = divmod(time, 100)
        m += 10
        if m &gt;= 60:
            h += 1
            m -= 60
        return h * 100 + m  # 올바른 출근 인정 시간 변환

    n = len(schedules)
    cont = 0  # 상품을 받을 직원 수

    for i in range(n):
        WSTime = schedules[i]  # 희망 출근 시간
        PSTime = convert_time(WSTime)  # 출근 인정 시간 변환
        is_success = True  

        day = startday  
        weekday_count = 0  # 평일 출근 횟수 카운트

        for j in timelogs[i]:
            if 1 &lt;= day &lt;= 5:  # 평일만 체크
                weekday_count += 1  
                if j &gt; PSTime:  # 지각한 경우
                    is_success = False  
                    break  

            day = 1 if day == 7 else day + 1  # 요일 업데이트

            if weekday_count == 5:  # 평일 5일 확인 후 중단
                break

        if weekday_count &lt; 5:  # 출근 기록이 부족하면 실패 처리
            is_success = False

        if is_success:
            cont += 1  



    return cont
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[2. 데이터 다루기]]></title>
            <link>https://velog.io/@tech_n0/2.-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%8B%A4%EB%A3%A8%EA%B8%B0</link>
            <guid>https://velog.io/@tech_n0/2.-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%8B%A4%EB%A3%A8%EA%B8%B0</guid>
            <pubDate>Sat, 15 Mar 2025 08:17:50 GMT</pubDate>
            <description><![CDATA[<h1 id="2-1-훈련-세트와-테스트-세트">2-1 훈련 세트와 테스트 세트</h1>
<h3 id="지도학습">지도학습</h3>
<ul>
<li>입력과 타깃으로 이뤄진 훈련 데이터 요구됨</li>
<li>데이터 == 입력</li>
<li>정답 == 타깃</li>
<li>훈련 데이터: 데이터와 타깃</li>
</ul>
<h3 id="비지도학습">비지도학습</h3>
<ul>
<li>입력 데이터만 사용</li>
<li>정답 사용X, 무언가를 맞힐 수 없음</li>
<li>데이터 파악, 변형에 도움</li>
</ul>
<p>챕터 1에서는 입력과 타깃이 있었음으로 지도  학습이였음</p>
<h3 id="테스트-세트">테스트 세트</h3>
<ul>
<li>평가에 사용되는 데이터</li>
</ul>
<h3 id="훈련-세트">훈련 세트</h3>
<ul>
<li>훈련에 사용되는 데이터</li>
</ul>
<p>머신 러닝의 정확한 평가 위해 테스트 세트와 훈련 세트는 따로 준비되어야 함</p>
<ul>
<li>훈련 세트와 테스트 세트를 준비해 보자</li>
</ul>
<pre><code class="language-python">fish_length = [25.4, 26.3, 26.5, 29.0, 29.0, 29.7, 29.7, 30.0, 30.0, 30.7, 31.0, 31.0, 
                31.5, 32.0, 32.0, 32.0, 33.0, 33.0, 33.5, 33.5, 34.0, 34.0, 34.5, 35.0, 
                35.0, 35.0, 35.0, 36.0, 36.0, 37.0, 38.5, 38.5, 39.5, 41.0, 41.0, 9.8, 
                10.5, 10.6, 11.0, 11.2, 11.3, 11.8, 11.8, 12.0, 12.2, 12.4, 13.0, 14.3, 15.0]
fish_weight = [242.0, 290.0, 340.0, 363.0, 430.0, 450.0, 500.0, 390.0, 450.0, 500.0, 475.0, 500.0, 
                500.0, 340.0, 600.0, 600.0, 700.0, 700.0, 610.0, 650.0, 575.0, 685.0, 620.0, 680.0, 
                700.0, 725.0, 720.0, 714.0, 850.0, 1000.0, 920.0, 955.0, 925.0, 975.0, 950.0, 6.7, 
                7.5, 7.0, 9.7, 9.8, 8.7, 10.0, 9.9, 9.8, 12.2, 13.4, 12.2, 19.7, 19.9]</code></pre>
<ul>
<li>각 생선의 길이와 무게를 하나의 리스트로 담은 2차원 리스트</li>
</ul>
<pre><code class="language-python">fish_data = [[l, w] for l, w in zip(fish_length, fish_weight)]
fish_target = [1] * 35 + [0] * 14</code></pre>
<ul>
<li><p>여기서 하나의 생선 데이터를 <strong>샘플</strong> 이라고 함!</p>
</li>
<li><p>사이킷런 의 K ~~클래스 임포트 후 모델 객체 생성</p>
</li>
</ul>
<pre><code class="language-python">from sklearn.neighbors import KNeighborsClassifier
kn = KNeighborsClassifier()</code></pre>
<ul>
<li><p>전체 데이터에서 처음 35개 선택해야 함</p>
</li>
<li><p>리스트 같이 배열 요소 선택할 떄는 인덱스(배열의 위치) 지정함</p>
</li>
<li><p>아래는 다섯번째 샘플을 출력하는 예제</p>
</li>
</ul>
<pre><code class="language-python">print(fish_data[4])</code></pre>
<ul>
<li>파이썬 리스트는 슬라이싱도 지원</li>
<li>슬라이싱: 인덱스 범위 지정해 원소 여러개 선택</li>
</ul>
<p>! 슬라이싱에서 마지막 인덱스 원소는 포함X</p>
<p>주의하삼</p>
<ul>
<li>다섯 번째 까지 출력하는 코드</li>
</ul>
<pre><code class="language-python">print(fish_data[0:5])</code></pre>
<ul>
<li>첫 인덱스부터 시작이면 아래처럼 생략 가능</li>
</ul>
<pre><code class="language-python">print(fish_data[:5])</code></pre>
<ul>
<li>마지막 원소 까지도 생략 가능( 끝 5개를 출력)</li>
</ul>
<pre><code class="language-python">print(fish_data[44:])</code></pre>
<ul>
<li>다시 본론으로 돌아와서  처음부터 35개의 샘플을 훈련 시키고 나머지를 테스트 세트로 하는 모델을 만들어보자</li>
<li></li>
</ul>
<pre><code class="language-python">train_input = fish_data[:35]
train_target = fish_target[:35]

test_input = fish_data[35:]
test_target = fish_target[35:]</code></pre>
<pre><code class="language-python">kn.fit(train_input, train_target)
kn.score(test_input, test_target)</code></pre>
<p>결과는 0.0 ……</p>
<aside>
💡

<p>왜 이런 결과가 나오지?</p>
</aside>

<ul>
<li>앞에 35개는 도미의 데이터</li>
<li>뒤에 나머지는 빙어의 데이터</li>
<li>도미로만 훈련하고 처음 보는 빙어로 테스트하는데 정확도가 있을리가 없다.</li>
<li>마치 국어 공부만 하고 수학 시험을 보는 것과 같다.</li>
</ul>
<aside>
💡

<p>그럼 어떻게 해?</p>
</aside>

<p>훈련 데이터와 테스트 데이터에 도미랑 방어가 골고루 섞여야함</p>
<ul>
<li>샘플링 편향:  훈련세트와 테스트 세트에 샘플이 고루 섞이지 않고 한쪽으로 치우친 상태</li>
</ul>
<p>이러한 데이터 비빔밥 맛집이 있다고 한다.</p>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/4aabc52f-6622-4ee3-809b-c0675e554e8e/image.png" alt=""></p>
<h3 id=""></h3>
<h1 id="넘파이">넘파이</h1>
<ul>
<li>py배열 라이브러리</li>
<li>고차원 배열을 쉽게 생성/조작 하는 도구 제공</li>
</ul>
<pre><code class="language-python">import numpy as np</code></pre>
<ul>
<li>py리스트를 넘파이 배열로 바꾸기</li>
</ul>
<pre><code class="language-python">input_arr = np.array(fish_data)
target_arr = np.array(fish_target)</code></pre>
<ul>
<li>출력해보자</li>
</ul>
<pre><code class="language-python">print(input_arr)</code></pre>
<pre><code class="language-python">[[  25.4  242. ]
 [  26.3  290. ]
 [  26.5  340. ]
 [  29.   363. ]
 [  29.   430. ]
 [  29.7  450. ]
 [  29.7  500. ]
 [  30.   390. ]
 [  30.   450. ]
 [  30.7  500. ]
 [  31.   475. ]
 [  31.   500. ]
 [  31.5  500. ]
 [  32.   340. ]
 [  32.   600. ]
 [  32.   600. ]
 [  33.   700. ]
 [  33.   700. ]
 [  33.5  610. ]
 [  33.5  650. ]
 [  34.   575. ]
 [  34.   685. ]
 [  34.5  620. ]
 [  35.   680. ]
 [  35.   700. ]
 [  35.   725. ]
 [  35.   720. ]
 [  36.   714. ]
 [  36.   850. ]
 [  37.  1000. ]
 [  38.5  920. ]
 [  38.5  955. ]
 [  39.5  925. ]
 [  41.   975. ]
 [  41.   950. ]
 [   9.8    6.7]
 [  10.5    7.5]
 [  10.6    7. ]
 [  11.     9.7]
 [  11.2    9.8]
 [  11.3    8.7]
 [  11.8   10. ]
 [  11.8    9.9]
 [  12.     9.8]
 [  12.2   12.2]
 [  12.4   13.4]
 [  13.    12.2]
 [  14.3   19.7]
 [  15.    19.9]]
</code></pre>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/69073de5-0a62-41ce-bb5c-a1c8667f18d1/image.png" alt=""></p>
<p>어때요, 참 쉽죠?</p>
<h2 id="shape">Shape</h2>
<ul>
<li>넘파이 배열의 크기를 알려주는 속성</li>
</ul>
<pre><code class="language-python">print(input_arr.shape)</code></pre>
<pre><code class="language-python">(49, 2)</code></pre>
<p>(샘플 수, 특성 수)</p>
<p>의 형태이다.</p>
<h3 id="랜덤-샘플-선택해-훈련-세트와-데스트-세트로-만들기">랜덤 샘플 선택해 훈련 세트와 데스트 세트로 만들기</h3>
<p>주의 사항</p>
<ul>
<li>input_arr 랑 targey_arr에서 같은 위치는 함께 선택되어야 함</li>
<li>타깃과 샘플이 함께 움직여야 함</li>
<li>그렇게 하려면 인덱스 값을 다 기억해야 하는데 그럼 효율 나쁨</li>
<li>그냥 인덱스를 섞어 기존의 인풋과 타겟에서 샘플을 선택하게 하자</li>
</ul>
<pre><code class="language-python">np.random.seed(42)
index = np.arange(49)
np.random.shuffle(index)</code></pre>
<ul>
<li>seed() 에 따라 다른 결과가 나오는데 통일하기 위해 42로 정함</li>
<li>arange()에 파라미터 N 전달 시 0~(N-1)까지 1씩 증가하는 배열 생성</li>
<li>shuffle()은 파라미터로 온 배열을 무작위로 섞음</li>
</ul>
<pre><code class="language-python">print(index)

# 곃과
[13 45 47 44 17 27 26 25 31 19 12  4 34  8  3  6 40 41 46 15  9 16 24 33
 30  0 43 32  5 29 11 36  1 21  2 37 35 23 39 10 22 18 48 20  7 42 14 28
 38]</code></pre>
<p>이븐하게 잘 비벼졌습니다.</p>
<h2 id="배열-인덱싱">배열 인덱싱</h2>
<ul>
<li>1개의 인덱스가 아닌 여러 개의 인덱스로 한번에 여러 개의 원소를 선택</li>
</ul>
<p>ex. 두번째와 네번째 샘플 선택</p>
<pre><code class="language-python">print(input_arr[[1, 3]])

# 결과
[[ 26.3 290. ]
 [ 29.  363. ]]</code></pre>
<ul>
<li>리스트 대신 넘파이 배열을 인텍스로도 전달 가능</li>
<li>input_arr와 target_arr에 랜덤 순서인 index를 인덱스로 사용하게 하여 35개 샘플을 훈련 세트로 만들고</li>
<li>나머지 뒷부분은 테스트 세트로 만들</li>
</ul>
<pre><code class="language-python">train_input = input_arr[index[:35]]
train_target = target_arr[index[:35]]

test_input = input_arr[index[35:]]
test_target = target_arr[index[35:]]</code></pre>
<ul>
<li>이제 산점도 그래프로 ㄱㄱ</li>
</ul>
<pre><code class="language-python"> import matplotlib.pyplot as plt
 plt.scatter(train_input[:,0], train_input[:,1])
 plt.scatter(test_input[:,0], test_input[:,1])
 plt.xlabel(&#39;length&#39;)
 plt.ylabel(&#39;weight&#39;)
 plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/9a2720a7-4e37-45de-9d66-cc4c2f8510a3/image.png" alt=""></p>
<h1 id="두-번째-머신러닝-프로그램">두 번째 머신러닝 프로그램</h1>
<ul>
<li>이제 K-최근접 이웃 모델을 훈련시키자</li>
<li>KNeighborsClassifier 클래스는 fit()매서드 실행할 때 마다 이전 학습 내역을 잃어버림</li>
<li>그대로 두고싶음 새 객체 만들어야함</li>
<li>근데 난 필요없으니 그대로 레츠고</li>
</ul>
<pre><code>kn.fit(train_input, train_target)
kn.score(test_input, test_target)</code></pre><pre><code class="language-python">1.0</code></pre>
<p>100% 다!</p>
<p>근데 수업에서 100%여도 마냥 좋은건 아니라 하신것 같은데</p>
<p>나중에 나오겠죠 뭐</p>
<ul>
<li>이제 predict() 메서드에 테스트 타깃을 넣어 예측하게 해봅세다</li>
</ul>
<pre><code class="language-python">kn.predict(test_input)

#결과
array([0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0])</code></pre>
<pre><code class="language-python">test_target

#결과
array([0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0])</code></pre>
<p>오늘의 작은 성공!</p>
<hr>
<h1 id="2-2-데이터-전처리">2-2 데이터 전처리</h1>
<p>하지만 이렇게 만든 모델도 나사가 조금 많이 빠진 녀석이다…</p>
<ul>
<li>길이 25, 무게 150인 도미를 빙어로 예측하는 문제가 발생</li>
</ul>
<p>다시 데이터 준비</p>
<pre><code class="language-python">fish_length = [25.4, 26.3, 26.5, 29.0, 29.0, 29.7, 29.7, 30.0, 30.0, 30.7, 31.0, 31.0, 
                31.5, 32.0, 32.0, 32.0, 33.0, 33.0, 33.5, 33.5, 34.0, 34.0, 34.5, 35.0, 
                35.0, 35.0, 35.0, 36.0, 36.0, 37.0, 38.5, 38.5, 39.5, 41.0, 41.0, 9.8, 
                10.5, 10.6, 11.0, 11.2, 11.3, 11.8, 11.8, 12.0, 12.2, 12.4, 13.0, 14.3, 15.0]
fish_weight = [242.0, 290.0, 340.0, 363.0, 430.0, 450.0, 500.0, 390.0, 450.0, 500.0, 475.0, 500.0, 
                500.0, 340.0, 600.0, 600.0, 700.0, 700.0, 610.0, 650.0, 575.0, 685.0, 620.0, 680.0, 
                700.0, 725.0, 720.0, 714.0, 850.0, 1000.0, 920.0, 955.0, 925.0, 975.0, 950.0, 6.7, 
                7.5, 7.0, 9.7, 9.8, 8.7, 10.0, 9.9, 9.8, 12.2, 13.4, 12.2, 19.7, 19.9]</code></pre>
<ul>
<li>전 방법은 파이썬 리스트를 순회하여 원소를 하나 씩 꺼내 생선의 길이와 무게를 담는 리스트를 만들었었다.</li>
<li>우린 이제 고급 노예인 넘파이가 있으니 딸깍이 가능함</li>
</ul>
<h2 id="column_stack">column_stack()</h2>
<ul>
<li>column_stack() 함수는 인자로 받은 리스트를 나란히 연결함</li>
</ul>
<pre><code class="language-python">import numpy as np
fish_data = np.column_stack((fish_length, fish_weight))
print(fish_data[:5])
# 결과 무계와 길이가 순서대로 짝지어짐
[[ 25.4 242. ]
 [ 26.3 290. ]
 [ 26.5 340. ]
 [ 29.  363. ]
 [ 29.  430. ]]</code></pre>
<p>함수 내부 구조는 안 뜯어봐서 효율이나 작동 방식은 안 찾아봤는데</p>
<p>시간이 너무 없으니 다음으로 기약 후 넘어가자…</p>
<p>이제 타겟  데이터를 만들어야 한다.</p>
<p>전에는 [1], [0]을 여러번 곱해 만들었는데 이제 함수사용해 날로먹자</p>
<h2 id="npones-npzeros">np.ones(), np.zeros()</h2>
<ul>
<li>각각 원하는 개수의 1 과 0으로만 이루어진 배열을 만드는 함수</li>
</ul>
<p>ex)숫자 1로만 이루어진 길이가 5인 배열</p>
<pre><code class="language-python">print(np.ones(5))

[1. 1. 1. 1. 1.]</code></pre>
<h2 id="npconcatenate">np.concatenate()</h2>
<ul>
<li>두 배열을 연결하는 함수</li>
<li>column_stack은 나란히 짝지주는 함수인데 요것은 길게 연결한다</li>
<li>도미 빙어 각각 35, 14마리씩 있으</li>
</ul>
<pre><code class="language-python">fish_target = np.concatenate((np.ones(35), np.zeros(14)))

print(fish_target)

[1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.
 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0.]</code></pre>
<p>근데 결과가 이진인데 float으로 할 필요가 있나? 메모리 낭비 같은데…</p>
<p>무튼 다음 단계로</p>
<h2 id="train_test_split">train_test_split()</h2>
<ul>
<li>사이킷런에서 제공하는 훈련/테스트 세트 나눠주는 함</li>
</ul>
<pre><code class="language-python">from sklearn.model_selection import train_test_split

train_input, test_input, train_target, test_target = train_test_split(fish_data, fish_target, random_state=42)</code></pre>
<p><code>random_state</code> 는 랜덤시드 지정 매개변수</p>
<ul>
<li>두개의 배열(fish_data, fish_target)을 전달했으니 2개씩 총 4개의 배열 반환 됨</li>
<li>기본적으로 25%를 테스트 세트로 분할</li>
</ul>
<pre><code class="language-python">print(train_input.shape, test_input.shape)
(36, 2) (13, 2)

print(train_target.shape, test_target.shape)
(36,) (13,)</code></pre>
<ul>
<li>잘 섞였는지도 테스트 해보자</li>
</ul>
<pre><code class="language-python">print(test_target)
[1. 0. 0. 0. 1. 1. 1. 1. 1. 1. 1. 1. 1.]</code></pre>
<ul>
<li>방어가 3마리밖에 안된다…</li>
<li>랜덤이기 때문에 골고루 섞이지 않을 수도 있어 샘플링 편항이 발생한것</li>
</ul>
<h2 id="stratify">stratify</h2>
<p>stratify 매개변수에 타깃 게이터를 전달하면 클래스 비율에 맞게 데이터를 나눠줌</p>
<pre><code class="language-python">train_input, test_input, train_target, test_target = train_test_split(fish_data, fish_target, stratify=fish_target, random_state=42)

print(test_target)

[0. 0. 1. 0. 1. 0. 1. 1. 1. 1. 1. 1. 1.]</code></pre>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/425764c3-cac4-4995-a202-64177ae34b41/image.png" alt=""></p>
<p>이븐하게 비벼진 모습</p>
<p>이제 다시 K-어쩌구 이웃 훈련을 하자</p>
<pre><code class="language-python">from sklearn.neighbors import KNeighborsClassifier

kn = KNeighborsClassifier()
kn.fit(train_input, train_target)
kn.score(test_input, test_target)

1.0</code></pre>
<p>모든 생선들을 바르게 분류했다 이제 전에 문제가 발생한  도미 데이터를 넣어보자</p>
<pre><code class="language-python">print(kn.predict([[25, 150]]))

[0.]</code></pre>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/194b84be-0c31-4560-8960-a2df8f3f2a2f/image.png" alt=""></p>
<p>왜 빙어라고 하는거야…</p>
<p>이 샘플을 다른 데이터와 함께 산점도로 확인해 보자</p>
<pre><code class="language-python">import matplotlib.pyplot as plt

plt.scatter(train_input[:,0], train_input[:,1])
plt.scatter(25, 150, marker=&#39;^&#39;)
plt.xlabel(&#39;length&#39;)
plt.ylabel(&#39;weight&#39;)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/7f2d29cc-5531-48ee-9361-57d138ede63a/image.png" alt=""></p>
<p>ㅏ….</p>
<ul>
<li>K-최근접 이웃은 주변 샘플 중 다수인 클래스로 예측함</li>
<li>그럼 이 문제 샘플의 주변 샘플을  확인해보자</li>
</ul>
<h2 id="kneighbors">kneighbors()</h2>
<ul>
<li>최근접 이웃을 찾아주는 매서드</li>
<li>이웃까지 거리, 이웃의 인덱스 반환</li>
<li>K-네이버 뭐시기의 클래스 이웃 기본 값은 5개 이므로 5개가 반환 될 것임</li>
</ul>
<pre><code class="language-python">distances, indexes = kn.kneighbors([[25, 150]])

plt.scatter(train_input[:,0], train_input[:,1])
plt.scatter(25, 150, marker=&#39;^&#39;)
plt.scatter(train_input[indexes,0], train_input[indexes,1], marker=&#39;D&#39;)
plt.xlabel(&#39;length&#39;)
plt.ylabel(&#39;weight&#39;)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/b1d006c5-78f8-4598-813d-bd85d9df880d/image.png" alt=""></p>
<pre><code class="language-python">print(train_target[indexes])

[[1. 0. 0. 0. 0.]]</code></pre>
<p>가까운 생선 중 4개가 빙어다.</p>
<p>거리 값도 확인하wk</p>
<pre><code class="language-python">print(distances)

[[ 92.00086956 130.48375378 130.73859415 138.32150953 138.39320793]]
</code></pre>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/0ee08257-5346-4bd6-ad18-158189ac67e1/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/7e7a5753-6ad2-47f4-af68-7b1a5ecc51ad/image.gif" alt=""></p>
<p>이렇게 떨어져 있는데 40정도밖에 차이가 안 난다고?</p>
<ul>
<li>여긴 함정이있다</li>
</ul>
<p>x축과 y축의 범위가 다르다.</p>
<ul>
<li>x축의 범위를 동일하게 맞춰서 출력해보자</li>
</ul>
<h2 id="xlim과-ylim">xlim()과 ylim()</h2>
<ul>
<li>해당 축 범위를 지정하는 함수</li>
</ul>
<pre><code class="language-python">plt.scatter(train_input[:,0], train_input[:,1])
plt.scatter(25, 150, marker=&#39;^&#39;)
plt.scatter(train_input[indexes,0], train_input[indexes,1], marker=&#39;D&#39;)
plt.xlim((0, 1000))
plt.xlabel(&#39;length&#39;)
plt.ylabel(&#39;weight&#39;)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/238bd4d9-7b1a-432b-b867-5d73f0058475/image.png" alt=""></p>
<p>이렇게 보니 생선의 키는 고만고만이라 별 영향력이 없어보인다.</p>
<ul>
<li>두 특성의 값이 놓인 범위가 매우 다른데 이를 두 특성의 <strong>스케일</strong>이 다르다고 한다.</li>
</ul>
<h2 id="데이터-전처리">데이터 전처리</h2>
<ul>
<li>특성 값을 일정한 기준으로 맞춰주는 작업</li>
</ul>
<p>특히 알고리즘이 거리 기반이면 데이터 전처리를 꼭 해주어야 올바른 예측이 가능하다.</p>
<h3 id="표준점수z점수">표준점수(z점수)</h3>
<ul>
<li>각 특성이 평균에서 표준편차의 몇 배만큼 떨어져 있는지 나타냄</li>
<li>가장 널리 사용하는 전처리 방법</li>
<li>실제 특성값의 크기에 상관없이 동일한 조건으로 비교 가능</li>
</ul>
<p>방법은 간단함</p>
<ul>
<li>평균을 배고 표준편차를 나누어줌</li>
<li>이것조차 귀찮다?</li>
<li>넘파이에서 함수를 제공한다…ㅎㅎ</li>
</ul>
<h2 id="npmean">np.mean()</h2>
<ul>
<li>평균 계산 함수</li>
</ul>
<h2 id="npstd">np.std()</h2>
<p>표준편차 계산 함수</p>
<pre><code class="language-python">mean = np.mean(train_input, axis=0)
std = np.std(train_input, axis=0)

print(mean, std)

[ 27.29722222 454.09722222] [  9.98244253 323.29893931]</code></pre>
<h3 id="axis의-의미"><code>axis</code>의 의미</h3>
<ul>
<li><code>axis=0</code>: 열(column) 단위 연산 → 각 <strong>특성(feature)별 평균, 표준편차</strong> 계산</li>
<li><code>axis=1</code>: 행(row) 단위 연산 → 각 <strong>샘플(sample)별 평균, 표준편차</strong> 계산</li>
<li><code>axis</code>를 지정하지 않으면 전체 배열을 기준으로 연산 수행</li>
</ul>
<p>특성마다 평균, 표준편차를 구했으니 원본 데이터에서 평균을 빼고 표준 편차로 나누어 표준 점수로 변환하자</p>
<pre><code class="language-python">train_scaled = (train_input - mean) / std</code></pre>
<p>이 식에서</p>
<ul>
<li>넘파이는 train_input의 모든 행에서 mean에 있는 두 평균값을 빼줌</li>
<li>그 후 std 에 있는 두 표준 편차를 모든 행에 적용</li>
<li>이런 넘파이 기능을 브로드캐스팅이라 부름</li>
</ul>
<h1 id="전터리-데이터로-모델-훈련">전터리 데이터로 모델 훈련</h1>
<pre><code class="language-python">plt.scatter(train_scaled[:,0], train_scaled[:,1])
plt.scatter(25, 150, marker=&#39;^&#39;)
plt.xlabel(&#39;length&#39;)
plt.ylabel(&#39;weight&#39;)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/7a90dae4-db50-43cd-80fb-c0c576b2c8c2/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/2ef608ed-6321-4a06-8348-76492afd38ad/image.png" alt=""></p>
<ul>
<li>왜 혼자 튀려하지?</li>
<li>왜냐하면 (25, 150) 이넘도 전처리 해주어야 함</li>
</ul>
<pre><code class="language-python">new = ([25, 150] - mean) / std
plt.scatter(train_scaled[:,0], train_scaled[:,1])
plt.scatter(new[0], new[1], marker=&#39;^&#39;)
plt.xlabel(&#39;length&#39;)
plt.ylabel(&#39;weight&#39;)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/c1f63983-25b7-4c39-8723-7fd71f905138/image.png" alt=""></p>
<ul>
<li>축 범위가 정상화 되어버린것을 알수있다.</li>
</ul>
<p>이제 다시 K-최근점 이웃 모델을 훈련하자!</p>
<pre><code class="language-python">kn.fit(train_scaled, train_target)
test_scaled = (test_input - mean) / std
kn.score(test_scaled, test_target)

1.0</code></pre>
<p>이제 문제의 케이스를 예측해 보자</p>
<pre><code class="language-python">print(kn.predict([new]))

[1.]</code></pre>
<p>성공이다</p>
<p>마지막으로 다시 k-최근점 이웃을 구하고 산점도로 그려보자</p>
<pre><code class="language-python">distances, indexes = kn.kneighbors([new])

plt.scatter(train_scaled[:,0], train_scaled[:,1])
plt.scatter(new[0], new[1], marker=&#39;^&#39;)
plt.scatter(train_scaled[indexes,0], train_scaled[indexes,1], marker=&#39;D&#39;)
plt.xlabel(&#39;length&#39;)
plt.ylabel(&#39;weight&#39;)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/5f5f3d11-23a1-43af-8eda-99928c56df55/image.png" alt=""></p>
<p>가까운 샘플은 모두 도미이다.</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/tech_n0/post/6282a7cb-659f-46dc-b788-51fc2f638ce6/image.png" width="430">
</p>

]]></description>
        </item>
        <item>
            <title><![CDATA[1. 나의 첫 머신러닝]]></title>
            <link>https://velog.io/@tech_n0/1.-%EB%82%98%EC%9D%98-%EC%B2%AB-%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D</link>
            <guid>https://velog.io/@tech_n0/1.-%EB%82%98%EC%9D%98-%EC%B2%AB-%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D</guid>
            <pubDate>Tue, 11 Mar 2025 13:57:38 GMT</pubDate>
            <description><![CDATA[<p>졸작으로 게임을 만들어서 출시하려 했는데</p>
<p>어찌저찌 하다 AI분야를 하게 되었다…</p>
<p>대회도 나가고 논문도 쓴다는데</p>
<p>새내기들 수업 조교도 하게 되었고</p>
<p>청소년 교육사업도 해야하는데</p>
<p>개인프로젝트로 언리얼 게임 만들기를 할 수 있을까…
1년만에 폭삭 늙어버리진 않을까...</p>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/ceb63a73-7704-4772-bbec-c9874b4bc72f/image.png" alt=""></p>
<p>전 말이죠… 게임 개발자가 되고 싶었어요…</p>
<h1 id="1-1">1-1</h1>
<h3 id="인공지능">인공지능</h3>
<ul>
<li>사람처럼 학습하고 추론할 수 있는 지능을 가진 컴퓨터 시스템을 만드는 기술</li>
</ul>
<h3 id="머신러닝">머신러닝</h3>
<ul>
<li>자동으로 데이터에서 규칙을 학습하는 알고리즘을 연구하는 분야</li>
</ul>
<h3 id="사이킷런">사이킷런</h3>
<ul>
<li>파이썬 기반에서 머신러닝 분석을 위해 사용할 수 있는 라이브러리</li>
<li>컴퓨터 과학 분야 대표 머신러닝 라이브러리</li>
</ul>
<h3 id="딥러닝">딥러닝</h3>
<p>머신러닝 알고리즘 중 인공 신경망 기반한 방법들의 통칭</p>
<h1 id="1-2">1-2</h1>
<p>코렐 기초니 넘어가도록…</p>
<h1 id="1-3">1-3</h1>
<h3 id="분류">분류</h3>
<ul>
<li>머신러닝에서 여러개의 종류(class)중 하나를 구별해 내는 문제</li>
<li>이진분류: 2개의 클래스 중 하나를 고르는 문제</li>
</ul>
<h3 id="특성">특성</h3>
<ul>
<li>데이터의 특징</li>
</ul>
<h3 id="산점도">산점도</h3>
<ul>
<li><p>x, y축으로 이루어진 좌표계에서 두 변수의 관계를 표현하는 방법</p>
</li>
<li><p>선형: 산점도 그래프가 일직선에 가까운 형태로 나타나는 경우</p>
</li>
<li><p>맷플롯립: 과학 계산용 그래프 패키지</p>
</li>
</ul>
<h3 id="도미-데이터로-산점도-그래프를-출력해-보자">도미 데이터로 산점도 그래프를 출력해 보자!</h3>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/14d844b9-2143-4ae0-90ab-0fa54ed37bf9/image.png" alt="스타듀밸리 하고싶다…">
스타듀밸리 하고싶다…</p>
<pre><code class="language-python">bream_length = [25.4, 26.3, 26.5, 29.0, 29.0, 29.7, 29.7, 30.0, 30.0, 30.7, 31.0, 31.0, 
                31.5, 32.0, 32.0, 32.0, 33.0, 33.0, 33.5, 33.5, 34.0, 34.0, 34.5, 35.0, 
                35.0, 35.0, 35.0, 36.0, 36.0, 37.0, 38.5, 38.5, 39.5, 41.0, 41.0]
bream_weight = [242.0, 290.0, 340.0, 363.0, 430.0, 450.0, 500.0, 390.0, 450.0, 500.0, 475.0, 500.0, 
                500.0, 340.0, 600.0, 600.0, 700.0, 700.0, 610.0, 650.0, 575.0, 685.0, 620.0, 680.0, 
                700.0, 725.0, 720.0, 714.0, 850.0, 1000.0, 920.0, 955.0, 925.0, 975.0, 950.0]</code></pre>
<pre><code class="language-python">import matplotlib.pyplot as plt

plt.scatter(bream_length, bream_weight)
plt.xlabel(&#39;length&#39;) # x축
plt.ylabel(&#39;weight&#39;) # y축
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/12094834-e7ce-4700-abc3-9042729e9970/image.png" alt=""></p>
<h3 id="그래프에-빙어-데이터를-추가하자">그래프에 빙어 데이터를 추가하자!</h3>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/420af7b4-e517-4443-92a9-48893d7c5591/image.png" alt=""></p>
<p>시샤모, 맛있었는데….  보고싶다…  다들</p>
<pre><code class="language-jsx">smelt_length = [9.8, 10.5, 10.6, 11.0, 11.2, 11.3, 11.8, 11.8, 12.0, 12.2, 12.4, 13.0, 14.3, 15.0]
smelt_weight = [6.7, 7.5, 7.0, 9.7, 9.8, 8.7, 10.0, 9.9, 9.8, 12.2, 13.4, 12.2, 19.7, 19.9]</code></pre>
<pre><code class="language-python">plt.scatter(bream_length, bream_weight)
plt.scatter(smelt_length, smelt_weight) # 빙어 데이터 추가
plt.xlabel(&#39;length&#39;)
plt.ylabel(&#39;weight&#39;)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/e2d212f9-eff6-4717-9022-c8e889d00683/image.png" alt=""></p>
<h2 id="첫-번째-머신러닝-프로그램">첫 번째 머신러닝 프로그램</h2>
<h3 id="k-최근점-이웃-알고리즘으로-도미와-빙어-데이터를-구분해-보자">K-최근점 이웃 알고리즘으로 도미와 빙어 데이터를 구분해 보자</h3>
<ul>
<li>우선 도미와 빙어 데이터를 하나의 데이터로 합치자</li>
</ul>
<pre><code class="language-python">length = bream_length + smelt_length
weight = bream_weight + smelt_weight</code></pre>
<ul>
<li>사이킷런 패키지를 쓸 건데 그러려면 각 특성의 리스트를 세로방향 2차원 리스트로 만들어야 함</li>
<li>파이썬의 zip 함수와 리스트 내포 구문을 사용하면 된다.</li>
</ul>
<h3 id="zip">Zip()</h3>
<p>나열된 리스트 각각에서 하나씩 원소를 꺼내 반환</p>
<pre><code class="language-python">fish_data = [[l,w] for l, w in zip(length, weight)]
print(fish_data)</code></pre>
<p>결과</p>
<pre><code class="language-python">[[25.4, 242.0], [26.3, 290.0], [26.5, 340.0], [29.0, 363.0], [29.0, 430.0], [29.7, 450.0], [29.7, 500.0], [30.0, 390.0], [30.0, 450.0], [30.7, 500.0], [31.0, 475.0], [31.0, 500.0], [31.5, 500.0], [32.0, 340.0], [32.0, 600.0], [32.0, 600.0], [33.0, 700.0], [33.0, 700.0], [33.5, 610.0], [33.5, 650.0], [34.0, 575.0], [34.0, 685.0], [34.5, 620.0], [35.0, 680.0], [35.0, 700.0], [35.0, 725.0], [35.0, 720.0], [36.0, 714.0], [36.0, 850.0], [37.0, 1000.0], [38.5, 920.0], [38.5, 955.0], [39.5, 925.0], [41.0, 975.0], [41.0, 950.0], [9.8, 6.7], [10.5, 7.5], [10.6, 7.0], [11.0, 9.7], [11.2, 9.8], [11.3, 8.7], [11.8, 10.0], [11.8, 9.9], [12.0, 9.8], [12.2, 12.2], [12.4, 13.4], [13.0, 12.2], [14.3, 19.7], [15.0, 19.9]]</code></pre>
<h3 id="정답-데이터-준비">정답 데이터 준비</h3>
<ul>
<li>컴퓨터는 문자를 직접 이해 불가</li>
<li>따라서 도미와 방어를 1과 0으로 표현</li>
</ul>
<pre><code class="language-python">fish_target = [1] * 35 + [0] * 14
print(fish_target)</code></pre>
<p>결과</p>
<pre><code class="language-jsx">[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]</code></pre>
<ul>
<li><p>사이킷 런의 K-최근접 이웃 알고리즘 일 구현한 KNeighborsClassifier 클래스를 임포트 하고</p>
</li>
<li><p>임포트 한 KNeighborsClassifier 클래스의 객체를 만든다.</p>
<pre><code class="language-python">  from sklearn.neighbors import KNeighborsClassifier

  kn =  KNeighborsClassifier()</code></pre>
</li>
</ul>
<ul>
<li>객체에 fish_data와 fish_target을 전달해 도미를 찾기 위한 기준을 학습 시킬것임</li>
<li>훈련: 모델에 데이터를 전달하여 규칙을 학습하는 과정</li>
</ul>
<pre><code class="language-python">kn.fit(fish_data, fish_target)
kn.score(fish_data, fish_target)</code></pre>
<ul>
<li>fit(): 주어진 데이터로 알고리즘을 훈련하는 매서드</li>
<li>score(): 모델 평가 매서드.<ul>
<li>1.0은 모든 데이터를 정확히 맞힘, 0.5는 절반</li>
<li>이 값을 정확도라고 함</li>
<li>이 코드에서 score 결과는 <code>1.0</code></li>
</ul>
</li>
</ul>
<h3 id="predict">predict</h3>
<ul>
<li>predict() 매서드: 새로운 데이터의 정답을 예측</li>
</ul>
<pre><code class="language-python">kn.predict([[30, 600]])</code></pre>
<p>결과는 <code>array([1])</code> </p>
<ul>
<li>K-최근접 이웃 알고리즘은 예측 시 가장 가까운 직선거리의 데이터만 살피면 됨</li>
<li>단점: 데이터가 아주 많은 경우 사용하기 어렵…</li>
<li>실제로 훈련 되는것이 없는 셈</li>
</ul>
<h3 id="가까운-몇개의-데이터를-참조하게-해보자">가까운 몇개의 데이터를 참조하게 해보자</h3>
<ul>
<li>KNeighborsClassifier의 기본값은 5임</li>
<li>가까운 49개 데이터를 참조해 예측하게 해보자</li>
</ul>
<pre><code class="language-python">kn49 =  KNeighborsClassifier(n_neighbors=49)

kn49.fit(fish_data, fish_target)
kn49.score(fish_data, fish_target)</code></pre>
<pre><code class="language-python">0.7142857142857143</code></pre>
<ul>
<li>별로 안정확 하다…</li>
<li>몇개를 참조시킬지 정하는것도 중요할듯</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[게임 실습 1. Unreal 게임 제작 기초]]></title>
            <link>https://velog.io/@tech_n0/%EA%B2%8C%EC%9E%84-%EC%8B%A4%EC%8A%B5-1.-Unreal-%EA%B2%8C%EC%9E%84-%EC%A0%9C%EC%9E%91-%EA%B8%B0%EC%B4%88</link>
            <guid>https://velog.io/@tech_n0/%EA%B2%8C%EC%9E%84-%EC%8B%A4%EC%8A%B5-1.-Unreal-%EA%B2%8C%EC%9E%84-%EC%A0%9C%EC%9E%91-%EA%B8%B0%EC%B4%88</guid>
            <pubDate>Mon, 10 Mar 2025 09:40:40 GMT</pubDate>
            <description><![CDATA[<h1 id="월드world">월드(World)</h1>
<ul>
<li>게임 콘텐츠 담기 위해 제공되는 가상공간</li>
<li>시간, 트랜스폼, 틱을 서비스로 제공</li>
<li>월드 세팅이라는 콘텐츠 제작 위한 기본 환경 설정 제공</li>
<li>월드 기본 단위는 액터로 정의, 액터 클래스 의 접두사는 A</li>
</ul>
<h1 id="게임-모드game-mode">게임 모드(Game Mode)</h1>
<ul>
<li>게임 규칙 지정, 게임 팡정 하는 최고 관리자 액터. 형태X</li>
<li>하나의 게임에는 하나의 게임 모드만</li>
<li>입장할 사용자 규격 지정 가능</li>
<li>멀티플레이 판정 처리하는 절대적 권위 심판</li>
</ul>
<h1 id="기믹">기믹</h1>
<ul>
<li>게임 진행 위한 이벤트 발생시키는 사물 액터</li>
<li>트리거: 이벤트 발생 위해 성정한 충돌 영역</li>
<li>트리거 통해 캐릭터와 상호작용, 월드에 액터 스폰해 콘텐츠 전개</li>
</ul>
<h1 id="플레이어">플레이어</h1>
<ul>
<li>게임 입장한 사용자 액터, 형태X</li>
<li>게임모드의 로그인 통해 사용자 게임월드 입장 시 플레이어 생성</li>
<li>싱글 플레이는 0번 플레이어 설정</li>
<li>사용자와 최종 커무니케이션 담당(입력장치 해석, 화면장치로 출력)</li>
</ul>
<h1 id="폰">폰</h1>
<ul>
<li>무형의 액터인 플레이어가 빙의해 조종하는 액터</li>
<li>길찾기 사용 가능, 기믹 및 다른 폰과 상호작용함</li>
<li>캐릭터: 인간형 폰</li>
</ul>
<hr>
<h1 id="실습">실습</h1>
<h1 id="1-월드설정--게임-모드-생성">1. 월드설정 &amp; 게임 모드 생성</h1>
<p>실습파일의 월드를 가져온후</p>
<p>클래스들을 만들어 줬는데 빌드에서 에러 발생</p>
<p>경로 지정이 안돼있기 때문</p>
<p>Build.cs 에 아래 경로 추가</p>
<pre><code class="language-cpp">PublicIncludePaths.AddRange(new string[] { &quot;ArenaBattle&quot; });</code></pre>
<h2 id="캐릭터-스폰">캐릭터 스폰</h2>
<p>언리얼에서 기본으로 제공하는 3인칭 게임 모드를 사용하면 딸깍으로 할 수도 있다</p>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/7709cc43-3f0c-43ec-8e34-e342782a552c/image.png" alt=""></p>
<p>하지만 딸깍으로는 좋은 개발자를 할 수 없으니 실습을 해보자</p>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/8de57ff9-f2f8-4ba2-a5bd-78b587b1e1bc/image.png" alt=""></p>
<p>빙의를 할 폰 클래스와 컨트롤러를 지정해주어야 함</p>
<p>설정을 하지 않아 사진에서는 선택이 비활성화 되어있음</p>
<ul>
<li>게임 모드 생성자에 해당 클래스 정보를 주면 언리얼 에디터가 자동으로 할당해줄 것임</li>
</ul>
<pre><code class="language-cpp">// Fill out your copyright notice in the Description page of Project Settings.

#include &quot;Game/ABGameModeBase.h&quot;
#include &quot;Player/ABPlayerController.h&quot;

AABGameModeBase::AABGameModeBase()
{
    //DefaultPawnClass

    PlayerControllerClass = AABPlayerController::StaticClass();
}
</code></pre>
<p>빌드를 해주면</p>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/248d2b6f-b726-4996-881d-1b87ad546de4/image.png" alt=""></p>
<p>플레이어 컨트롤러 클래스가 변경되었다!</p>
<p>이제 플레이어가 빙의 할 폰을 언리얼에서 제공하는 3인칭 마네킹으로 설정해야함</p>
<ul>
<li>에셋 주소를 가져오자</li>
</ul>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/8c7a3e86-5d5d-4fc3-b203-9a62ad9be0ec/image.png" alt=""></p>
<ul>
<li>static ConstructorHelpers::FClassFinder<APawn> 으로 ThirdPersonClassRef 에 복사한 주소를 넣어주는데 경로 마지막에 _C를 붙여줘야 함(클래스 정보를 가져올 것이기 때문)</li>
<li><APawn> : 폰에서 상속받았기 때문</li>
</ul>
<pre><code class="language-cpp">// Fill out your copyright notice in the Description page of Project Settings.

#include &quot;Game/ABGameModeBase.h&quot;
#include &quot;Player/ABPlayerController.h&quot;

AABGameModeBase::AABGameModeBase()
{
    static ConstructorHelpers::FClassFinder&lt;APawn&gt;ThirdPersonClassRef(TEXT(&quot;/Script/Engine.Blueprint&#39;/Game/ThirdPerson/Blueprints/BP_ThirdPersonCharacter.BP_ThirdPersonCharacter_C&#39;&quot;));
    // 주소 끝에 _C 붙여줘야함
    if (ThirdPersonClassRef.Class) // 클래스 정보가 null이 아니면
    {
        DefaultPawnClass = ThirdPersonClassRef.Class;
    }

    PlayerControllerClass = AABPlayerController::StaticClass();
}
</code></pre>
<p>빌드하고 실행해 보면</p>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/13a3c57a-ca16-4343-87ec-66e58e7c630e/image.png" alt=""></p>
<p>성공!</p>
<h2 id="abplayercontroller">ABPlayerController</h2>
<ul>
<li>생성만 해뒀던 ABPlayerController를 사용해 시작 시 마우스 입력이 바로 뷰포트로 들어가게 설정하기</li>
</ul>
<p>이게 무슨소리지?</p>
<ul>
<li><p>아마 언리얼에서 플레이 버튼 눌러도 뷰포트를 한번 클릭하지 않으면 캐릭터 조작이 불가능 했는데 플레이 버튼만 누르면 바로 조작하게  설정하는 것 같음</p>
</li>
<li><p>게임 시작 시 마우스 인풋을 뷰포트로 옮겨주는 코드를 작성하자</p>
</li>
</ul>
<p><code>ABPlayerController.h</code></p>
<p>BeginPlay 라는 함수를 오버드라이브 해 구현하자</p>
<pre><code class="language-cpp">// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include &quot;CoreMinimal.h&quot;
#include &quot;GameFramework/PlayerController.h&quot;
#include &quot;ABPlayerController.generated.h&quot;

/**
 * 
 */
UCLASS()
class ARENABATTLE_API AABPlayerController : public APlayerController
{
    GENERATED_BODY()

protected:
    virtual void BeginPlay() override; // 

};
</code></pre>
<p><code>ABPlayerController.cpp</code> </p>
<pre><code class="language-cpp">// Fill out your copyright notice in the Description page of Project Settings.

#include &quot;Player/ABPlayerController.h&quot;

void AABPlayerController::BeginPlay()
{
    Super::BeginPlay();

    FInputModeGameOnly GameOnlyInputMode;
    SetInputMode(GameOnlyInputMode);
}</code></pre>
<p>이제 시작하자마자 포커스가 뷰포트로 들어감</p>
<h2 id="게임모드에서-헤더로-인클루드-안하고-abplayercontroller-설정하기">게임모드에서 헤더로 인클루드 안하고 ABPlayerController 설정하기</h2>
<ul>
<li>생성한 C++ 객체들은 고유 경로를 가지고 있음</li>
</ul>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/1db52314-30bc-4e63-b48e-912e39688ef4/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/1b50c949-9c0b-4d97-9491-fa46390c9b57/image.png" alt=""></p>
<p>3인칭 캐릭터에 했던것 처럼 이 주소를 사용하면</p>
<p>해더를 인클루드 안해도 클래스 정보 얻어올 수 있다</p>
<pre><code class="language-cpp">// Fill out your copyright notice in the Description page of Project Settings.

#include &quot;Game/ABGameModeBase.h&quot;
#include &quot;Player/ABPlayerController.h&quot;

AABGameModeBase::AABGameModeBase()
{
    static ConstructorHelpers::FClassFinder&lt;APawn&gt;ThirdPersonClassRef(TEXT(&quot;/Game/ThirdPerson/Blueprints/BP_ThirdPersonCharacter.BP_ThirdPersonCharacter_C&quot;));
    // 주소 끝에 _C 붙여줘야함
    if (ThirdPersonClassRef.Class) // 클래스 정보가 null이 아니면
    {
        DefaultPawnClass = ThirdPersonClassRef.Class;
    }

// 추가
    static ConstructorHelpers::FClassFinder&lt;APlayerController&gt; PlayerControllerClassRaf(TEXT(&quot;/Script/ArenaBattle.ABPlayerController&quot;));
    // 클래스 정보가 복제된 것이기 때문에 _C 안함
    if (PlayerControllerClassRaf.Class)
    {
        PlayerControllerClass = PlayerControllerClassRaf.Class;
    }
// --------------------------------

    PlayerControllerClass = AABPlayerController::StaticClass();
}
</code></pre>
<p>이 방식은 헤더파일 의존도를 낮출 수 있음</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[13. 언리얼 오브젝트 관리 I - 직렬화]]></title>
            <link>https://velog.io/@tech_n0/13.-%EC%96%B8%EB%A6%AC%EC%96%BC-%EC%98%A4%EB%B8%8C%EC%A0%9D%ED%8A%B8-%EA%B4%80%EB%A6%AC-I-%EC%A7%81%EB%A0%AC%ED%99%94</link>
            <guid>https://velog.io/@tech_n0/13.-%EC%96%B8%EB%A6%AC%EC%96%BC-%EC%98%A4%EB%B8%8C%EC%A0%9D%ED%8A%B8-%EA%B4%80%EB%A6%AC-I-%EC%A7%81%EB%A0%AC%ED%99%94</guid>
            <pubDate>Mon, 10 Mar 2025 09:36:38 GMT</pubDate>
            <description><![CDATA[<h1 id="직렬화가-무야">직렬화가 무야</h1>
<ul>
<li>오브젝트나 연결된 오브젝트의 묶음(오브젝트 그래프)을 바이트 스트림으로 변환하는 과정<ul>
<li>복잡한 데이터를 일렬로 세우기 때문</li>
</ul>
</li>
<li>복구시키는 과정도 포함<ul>
<li>시리얼라이제이션: 오브젝트 그래프 → 바이트 스트림 으로</li>
<li>디시리얼라이제이션: 바이트 스트림 → 오브젝트 그래프 로</li>
</ul>
</li>
<li>장점<ul>
<li>현재 프로그램 상태 저장해 필요시 복원 가능(게임 저장)</li>
<li>현재 객체의 정보를 클립보드에 복사해 다른 프로그램 전송 가능</li>
<li>네트워크 통해 현재 프로그램 상태 다른  PC에서 복원 가능(멀티플레이)</li>
<li>데이터 압축, 압호화 통해 데이터 효율적, 안전하게 보관</li>
</ul>
</li>
</ul>
<p>직렬화 구현 시 고려사항</p>
<ul>
<li>데이터 레이아웃: 오브젝트가 소유한 데이터를 변화할 건지</li>
<li>이식성: 서로 다른 시스템에 전송해도 이식 가능한지</li>
<li>버전 관리: 기능 추가 시 어떻게 확장하고 처리할 건지</li>
<li>성능: 네트웍 비용 줄이기 위해 어떤 데이터 형식 사용할 건지</li>
<li>보안: 데이터 어떻게 보호할건지</li>
<li>에러 처리: 전송과정 문제 발생 시 어떻게 인식하고 처리할 건지</li>
</ul>
<p>언리얼은 이걸 모두 고려한 직렬화 시스템 자체 제공</p>
<h1 id="언리얼-직렬화-시스템">언리얼 직렬화 시스템</h1>
<ul>
<li>FArchive<ul>
<li>직렬화 시스템을 위해 제공하는 클래스</li>
<li>Shift(&lt;&lt;) 연산자 사용해 전송시  원하는 바이트 스트림 저장 가능</li>
</ul>
</li>
<li>다양한 아카이브 클래스 제공<ul>
<li>메모리 아카이브</li>
<li>파일 아카이브</li>
<li>기타 언리얼 오브젝트 관련 아카이브 클래</li>
</ul>
</li>
<li>Json 직렬화 기능: 별도 라이브러리 통해 제공</li>
</ul>
<h1 id="">+</h1>
<h1 id="12-언리얼-엔진의-메모리-관리">12. 언리얼 엔진의 메모리 관리</h1>
<h1 id="c">C++</h1>
<ul>
<li>메모리를 포인터 활용해 관리</li>
<li>new , dekete쌍을 지켜주지 않으면 터짐</li>
</ul>
<h1 id="가비지-컬렉션">가비지 컬렉션</h1>
<ul>
<li>더이상 사용되지 않는다 판단된 메모리를 회수</li>
<li>동적 생성된 오브젝트 정보를 모아둔 저장소 이용해 미사용 메모리 추적</li>
<li>언리얼은 마크 스왑 방식으로 가비지 컬렉션 동작</li>
<li>지정 시간마다 수행하는 방식</li>
</ul>
<p>UPROPERTY로 참조된 언리얼 오브젝트, 루트셋으로 지정된 언리얼 오브젝는 가비지 컬렉터가 회수 안함</p>
<h1 id="14-언리얼-오브젝트-관리-ii---패키지">14. 언리얼 오브젝트 관리 II - 패키지</h1>
<h1 id="패키지">패키지</h1>
<ul>
<li>언리얼 오브젝트를 감싼 포장 오브젝트</li>
<li>개발된 최종 콘텐츠를 정리해 프로그램으로 만드는 작업 ex) 게임 패키징</li>
<li>DLC 등 향후 확장 콘텐츠에 사용되는 별도 데이터 묶음</li>
</ul>
<h1 id="언리얼-오브젝트">언리얼 오브젝트</h1>
<ul>
<li>다수의 언리얼  오브젝트를 포장하는데 사용ㅎ는 언리얼 오브젝트<ul>
<li>모든 언리얼 오브젝트는 패키지 소속</li>
</ul>
</li>
<li>애셋: 언리얼 오브젝트 패키지의 서브 오브젝트<ul>
<li>에디터에는 에셋들이 노출됨</li>
</ul>
</li>
<li>구조상 패키지는 다수 언리얼 오브젝트 소유 가능, 일반적으로 하나의 애셋만 가짐</li>
<li>애셋은 다수 서브 오브젝트 가질수 있음, 모두 언리얼 오브젝트 패키지에 포함<ul>
<li>but, 에디터 노출 X</li>
</ul>
</li>
</ul>
<p>트랜지언트 패키지: 임시 패키지</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[5. 언리얼 오브젝트 리플렉션 시스템I]]></title>
            <link>https://velog.io/@tech_n0/5.-%EC%96%B8%EB%A6%AC%EC%96%BC-%EC%98%A4%EB%B8%8C%EC%A0%9D%ED%8A%B8-%EB%A6%AC%ED%94%8C%EB%A0%89%EC%85%98-%EC%8B%9C%EC%8A%A4%ED%85%9CI</link>
            <guid>https://velog.io/@tech_n0/5.-%EC%96%B8%EB%A6%AC%EC%96%BC-%EC%98%A4%EB%B8%8C%EC%A0%9D%ED%8A%B8-%EB%A6%AC%ED%94%8C%EB%A0%89%EC%85%98-%EC%8B%9C%EC%8A%A4%ED%85%9CI</guid>
            <pubDate>Mon, 10 Mar 2025 09:34:29 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-cpp">    UE_LOG(LogTemp, Log, TEXT(&quot;=========================&quot;));

    UClass* ClassRuntime = GetClass(); //UMyGameInstance 클래스 정보를 런타임에서 얻어옴
    UClass* ClassCompile = UMyGameInstance::StaticClass(); // 해당 클래스 정보 얻어오는 다른 방법 위와 동일한 객체 가르킴

    //check(ClassRuntime == ClassCompile);
    // 체크 통과하면 바로 아래의 로그를 출력 하지만 체크는 실패 발생시 에디터가 꺼지고 에러 출력

    //ensure(ClassRuntime != ClassCompile);
    // ensure는 에디터 안끄고 문제점 확인 가능

    ensureMsgf(ClassRuntime != ClassCompile, TEXT(&quot;에러 발생 시킨 코드&quot;));
    //ensureMsgf는 메시지 출력 가능

    UE_LOG(LogTemp, Log, TEXT(&quot;학교 담당 클래스 이름 : %s&quot;), *ClassRuntime-&gt;GetName());

    UE_LOG(LogTemp, Log, TEXT(&quot;=========================&quot;));</code></pre>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/5db6a97b-430a-4644-b48c-0aa9cdab2bac/image.png" alt=""></p>
<hr>
<h1 id="cdoclassdefaultobject">CDO(ClassDefaultObject)</h1>
<p>기본값을 지정하지 않으면 빈 문자열</p>
<p>기본값을 지정하고 싶음 생성자 코드에 지정하면 된다.</p>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/efd6cd2e-eed2-4c65-acde-c8d4e009bea2/image.png" alt=""></p>
<p>이부분</p>
<pre><code class="language-cpp">UMyGameInstance::UMyGameInstance()
{
    // 롸잇 히어ㄹ
    MyNamel = TEXT(&quot;기본값&quot;);
}</code></pre>
<p>근데 아래에서 이 변수를 MyNamel 을 바꾸면 기본값은 없어지나?</p>
<pre><code class="language-cpp">void UMyGameInstance::Init()
{
    MyNamel = TEXT(&quot;바밤바&quot;);
}</code></pre>
<p>정답은 NOPE.</p>
<p>기본값은 CDO라고 하는 템플릿 객체에 저장됨</p>
<pre><code class="language-cpp">UMyGameInstance::UMyGameInstance()
{
    MyNamel = TEXT(&quot;기본값&quot;);
}

void UMyGameInstance::Init()
{
    Super::Init();
    MyNamel = TEXT(&quot;바밤바&quot;);

    MyNamel= TEXT(&quot;벼볌벼&quot;);

    UE_LOG(LogTemp, Log, TEXT(&quot;이름 : %s&quot;), *MyNamel); // 바밤바 출력됨

    UE_LOG(LogTemp, Log, TEXT(&quot;이름 기본 값 : %s&quot;), *GetClass()-&gt;GetDefaultObject&lt;UMyGameInstance&gt;()-&gt;MyNamel);
    // 기본값인 벼볌벼출...이 될까?

}</code></pre>
<p>하지만 기본값은 출력이 안됨</p>
<p>왜냐하면</p>
<p>CDO(기본값)를 고치는 생성자 코드 수정시 헤터파일 수정과 같이 에디터를 끄고 컴파일 해야 함</p>
<p>CDO와 UClass 정보는 엔진이 초기되 되는 과정에서 정보가 만들어지기 때문</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[4. 언리얼 오브젝트 기초]]></title>
            <link>https://velog.io/@tech_n0/4.-%EC%96%B8%EB%A6%AC%EC%96%BC-%EC%98%A4%EB%B8%8C%EC%A0%9D%ED%8A%B8-%EA%B8%B0%EC%B4%88</link>
            <guid>https://velog.io/@tech_n0/4.-%EC%96%B8%EB%A6%AC%EC%96%BC-%EC%98%A4%EB%B8%8C%EC%A0%9D%ED%8A%B8-%EA%B8%B0%EC%B4%88</guid>
            <pubDate>Mon, 10 Mar 2025 09:32:54 GMT</pubDate>
            <description><![CDATA[<p>게임 프로그래밍은 사용자 관점에서 단일 컴퓨터로 최대 성능을 뽑아내는게 목표</p>
<p>개발자 관점에서는 게임의 규모가 커질수록 방대하고 복잡한 기능을 안정적으로 관리</p>
<p>C++ 단점</p>
<ol>
<li>어렵…</li>
<li>하드웨어를 직접 제어하니 실수하면 큰일남..!</li>
</ol>
<hr>
<h1 id="모던-객체-지향-설계-원칙">모던 객체 지향 설계 원칙</h1>
<p>SOLID</p>
<p>S : Single Responsibility Principle : 단일 책임 원칙 : 하나의 클래스는 하나의 기능만.</p>
<p>O : Open and Closed Principle : 개방-폐쇄 원칙 : 확장에는 열려 있어야 하나, 변경에는 닫히게,</p>
<p>L : Liskov substitution Principle : 리스코브 치환 원칙 : 자식 클래스는 부모 클래스로 사용 가능</p>
<p>I : Interface segregation Principle : 인터페이스 분리 원칙 : 작고 명확한 인터페이스를 분리해야 함.</p>
<p>D : Dependency inversion Principle : 의존관계 역전 원칙 :</p>
<hr>
<h1 id="언리얼-오브젝트">언리얼 오브젝트</h1>
<p>성능을 위해 C++을 쓰면서 객체지향을 위해 확장함</p>
<p>그게 바로 언리얼 오브젝트</p>
<p>공식문서: <a href="https://docs.unrealengine.com/5.1/ko/objects-in-unreal-engine/">https://docs.unrealengine.com/5.1/ko/objects-in-unreal-engine/</a></p>
<aside>
⚙

<h2 id="object-에-제공되는-함수성"><strong>Object 에 제공되는 함수성</strong></h2>
<p>모든 경우에 이 시스템을 사용하는 것이 필수적이지도 심지어 적절하지도 않지만, 그렇게 해 주면 다음과 같은 장점이 있습니다:</p>
<ul>
<li>가비지 컬렉션</li>
<li>레퍼런스 업데이트</li>
<li>리플렉션</li>
<li>시리얼라이제이션</li>
<li>디폴트 프로퍼티 변경사항 자동 업데이트</li>
<li>자동 프로퍼티 초기화</li>
<li>자동 에디터 통합</li>
<li>실행시간에 유형 정보 사용가능</li>
<li>네트워크 리플리케이션</aside></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[3. 언리얼 C++ 기본타입과 문자열]]></title>
            <link>https://velog.io/@tech_n0/3.-%EC%96%B8%EB%A6%AC%EC%96%BC-C-%EA%B8%B0%EB%B3%B8%ED%83%80%EC%9E%85%EA%B3%BC-%EB%AC%B8%EC%9E%90%EC%97%B4</link>
            <guid>https://velog.io/@tech_n0/3.-%EC%96%B8%EB%A6%AC%EC%96%BC-C-%EA%B8%B0%EB%B3%B8%ED%83%80%EC%9E%85%EA%B3%BC-%EB%AC%B8%EC%9E%90%EC%97%B4</guid>
            <pubDate>Mon, 10 Mar 2025 09:32:23 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/tech_n0/post/4852450e-d2ee-49aa-afe2-b0ad64a88483/image.png" alt="">
헤더에서 Init을 선언하고 줄 번호 표시 옆의 드라이버 아이콘으로 cpp파일에 해당 함수를 자동완성으로 오버라이드 가능</p>
<p>(또는 Init을 드래그 한후 Alt + Enter)</p>
<h1 id="fstring">FString</h1>
<p>문자열을 다양하게 조작하고 싶으면 TCHAR 말고 FString 클래스 사용</p>
<p>언리얼은 유니코드를 사용해 문자열 처리를 통일</p>
<ul>
<li>2byte로 사이즈가 균일한 UTF-16 사용</li>
<li>유니코드를 위한 언리얼 표준 캐릭터 타입: TCHAR</li>
</ul>
<p>문자열은 항상 TEXT 매크로 사용해 지정</p>
<ul>
<li>TEXT 매크로로 감싼 문자열은 TCHAR 배열로 지정됨</li>
</ul>
<p>문자열 다루는 클래스로 FString 제공</p>
<ul>
<li>FString: TCHAR 배열을 포함하는 헬퍼 클래스</li>
</ul>
<p>문자열 찾거나 자르는 건 FCString을 통해 진행</p>
<h1 id="fname">FName</h1>
<p>에셋 관리용 문자열 체계</p>
<ul>
<li>대소구분X</li>
<li>한번 선언되면 수정X</li>
<li>가볍고 빠름</li>
<li>문자 표현용이 아닌 에셋 키 저장용, 빌드 시 해시값으로 변환\</li>
<li>자주 실행되는 함수에서 사용하면 오버헤드 발생할 수 있음</li>
</ul>
<h1 id="ftext">FText</h1>
<p>다국어 지원을 위한 문자열 체계</p>
<ul>
<li>일종의 키로 작동</li>
<li>별도 문자열 테이블 정보가 추가로요구됨</li>
<li>게임 빌드 시 자동으로 다양한 국가별 언어로 변환됨</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[OS : 22. 물리 메모리 크기의 극복 : 정책]]></title>
            <link>https://velog.io/@tech_n0/OS-22.-%EB%AC%BC%EB%A6%AC-%EB%A9%94%EB%AA%A8%EB%A6%AC-%ED%81%AC%EA%B8%B0%EC%9D%98-%EA%B7%B9%EB%B3%B5-%EC%A0%95%EC%B1%85</link>
            <guid>https://velog.io/@tech_n0/OS-22.-%EB%AC%BC%EB%A6%AC-%EB%A9%94%EB%AA%A8%EB%A6%AC-%ED%81%AC%EA%B8%B0%EC%9D%98-%EA%B7%B9%EB%B3%B5-%EC%A0%95%EC%B1%85</guid>
            <pubDate>Tue, 18 Feb 2025 09:43:37 GMT</pubDate>
            <description><![CDATA[<ul>
<li>비어 있는 메모리가 많으면 페이지 폴트 발생 시 빈 페이지 리스트에서 빈 페이지를 찾아 폴트 발생한 페이지에 할당하면 됨</li>
<li>빈 메모리 공간이 거의 없으면 OS는 메모리 압박 해소를 위해 다른 페이지를 강제로 페이징 아웃 해 공간 확보</li>
<li>내보낼 페이지 선택은 OS의 교체 정책 안에 집약 됨</li>
</ul>
<h1 id="221-캐시-관리">22.1 캐시 관리</h1>
<ul>
<li><p>메인 메모리는 시스템의 가상 메모리 페이지를 가져다 놓기 위한 캐시로 생각할 수 있음</p>
</li>
<li><p>캐시를 위한 교체 정책 목표: 캐시 미스 최소화(디스크로 부터 페이지 가져오는 횟수 최소)</p>
</li>
<li><p>키시 히트 횟수를 최대로 하는것도 목표(접근된 페이지가 메모리에 이미 존재하는 횟수를 최대로)</p>
</li>
<li><p>평균 메모리 접근 시간(average memory access time. AMAT):</p>
<ul>
<li><p>컴퓨터 아키텍트가 하드웨어 캐시 성능을 측정 시 사용하는 미터법</p>
</li>
<li><p>$AMAT = (P_{Hit} ⋅ T_M) + (P_{Miss} ⋅ T_D)$</p>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/7f8f0612-3003-4d33-b807-67a9722349e5/image.png" alt=""></p>
</li>
</ul>
</li>
</ul>
<aside>
⚙

<p>$T_M$: 메모리 비 접근 비용</p>
<p>$T_D$: 디스크 접근 비용</p>
<p>$P_{Hit}$: 캐시에서 데이터 항목을 찾을 확률</p>
<p>$P_{Miss}$: 캐시에서 데이터를 못찾을 확율</p>
</aside>

<ul>
<li>히트와 미스는 각각 0.0에서 1.0 가이의 값을 가지고 두 합은 1.0을 만족</li>
</ul>
<h1 id="222-최적-교체-정책">22.2 최적 교체 정책</h1>
<ul>
<li>미스를 최소화 하는 정첵</li>
<li>가장 먼 시점에 접근할 페이지를 버림</li>
<li>구현 HARD</li>
</ul>
<p>ex) </p>
<ul>
<li>프로그램이 0, 1, 2, 0, 1, 3, 0, 3, 1, 2, 1의 순서대로 가상 페이지들을 접근</li>
<li>캐시에 세 개 페이지 저장 가능</li>
</ul>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/7cb1c16d-6fc5-48e3-91f0-006fe31fa8a1/image.png" alt=""></p>
<ul>
<li>캐시는 빈상태로 시작하기 떄문에 첫 세번은 미스(최초 시작 미스 or 강제 미스)</li>
<li>3을 만나면 3은 캐시에 없음으로 미스</li>
<li>2을 가장 나중에 접근함으로 2를 버림</li>
</ul>
<p>히트 6번, 미스 5번 이니 히트율: 54.5%</p>
<p>강제 미스 제외 히트율: 85.7%</p>
<h1 id="223-간단한-정책--fifo">22.3 간단한 정책 : FIFO</h1>
<ul>
<li><p>선입선출(먼저 들어온 것이 먼저 나감)</p>
</li>
<li><p>시스템에 페이지 들어오면 큐에 삽입</p>
</li>
<li><p>교체해야 할 경우 큐의 테일의 페이지가 내보내짐</p>
</li>
<li><p>구현 EASY</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/27491f38-5fb4-4f41-894f-32d721538503/image.png" alt=""></p>
<p>히트율: 36.4%</p>
<p>강제 미스 제외 히트율: 57.1%</p>
<h1 id="224-또-다른-간단한-정책--무작위-선택">22.4 또 다른 간단한 정책 : 무작위 선택</h1>
<p>랜덤 게임</p>
<ul>
<li>메모리 압박 발생 시 무작위로 페이지 선택해 교체</li>
<li>구현EASY</li>
<li>내보낼 블럭 제대로 선택하려는 노오력을 하지 않음</li>
<li>운빨게임이지만 FIFO보다 약간 더 좋은 성능</li>
<li>최적의 방법보단 약간 나쁜 성능</li>
</ul>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/cde7f2f6-915d-45bc-871a-48a465651341/image.png" alt=""></p>
<p>동작 방식은 그때 그때 달라짐</p>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/00c5abfa-47d0-4808-a091-3c6cf9b6e585/image.png" alt=""></p>
<h1 id="225-과거-정보의-사용--lru">22.5 과거 정보의 사용 : LRU</h1>
<p>FIFO, 무작위는 중요한 페이지 또는 바로 다시 참조할 페이지를 내보낼 수 있는 문제가 있음</p>
<ul>
<li>페이지 교체 정책이 활용할 수 있는 과거 정보<ul>
<li>빈도수: 한 페이지가 여러차레 접근되었으면 어떤 가치가 있기 때문</li>
<li>최근성: 최근에 접근 된 페이지일수록 가까운 미래에 접근 될 확률 높음</li>
</ul>
</li>
<li>이런 정책은 지역성의 원칙을 기반에 둔다</li>
<li>지역성의 원칙<ul>
<li>프로그램은 특정 코드와 자료 구조를 빈번하게 접근하는 경향 있음</li>
<li>과거의 현상을 보고 중요한 페이지 판단후 내보낼 페이지 선택 시 중요 페이지는 메모리에 보존</li>
</ul>
</li>
</ul>
<h2 id="교체-알고리즘">교체 알고리즘</h2>
<ul>
<li>LeastFrequently-Used(LFU):  가장 적은 빈도로 사용된 페이지를 교체</li>
<li>LeastRecently-Used(LRU):  가장 오래 전에 사용하였던 페이지를 교체</li>
</ul>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/5adbe16d-14bf-44e2-865f-0959ce63e2d4/image.png" alt=""></p>
<ul>
<li>반대 경우인 Most-Frequently-Used(MFU)와 Most-Recently-Used(MRU)
와 같은 알고리즘도 존재. 하지만 잘 동작하지 않음</li>
</ul>
<h1 id="226-워크로드에-따른-성능-비교">22.6 워크로드에 따른 성능 비교</h1>
<p>공간 지역성: 어떤 페이지 P 가 접근되었으면 그 페이지 주변의 페이지들이 참조되는 경향</p>
<p>시간 지역성: 가까운 과거에 참조되었던 페이지는 가까운 미래에 다시 접든되는 경향</p>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/98443949-b241-456f-962f-f132421f15d3/image.png" alt=""></p>
<ul>
<li>지역성이 없다면 어느 정책을 사용하든 상관X</li>
</ul>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/59ab2e5b-789f-4b49-924b-b79c7f5d060b/image.png" alt=""></p>
<ul>
<li>20% 페이지들에서 80%의 참조가 발생</li>
<li>나머지 80% 페이지 들에서는 20% 참조 발생</li>
<li>파레토 법칙이랑 비슷한 경우같음</li>
<li>LRU의 과거 정보가 완벽하지 않다는 것을 의미</li>
<li>상황따라 좋은 방법이 다르다</li>
</ul>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/885336e5-7b21-45aa-80ec-22bff22d3cbe/image.png" alt=""></p>
<ul>
<li>50개 페이지를 순차적 참조하는 경우</li>
</ul>
<h1 id="227-과거-이력-기반-알고리즘의-구현">22.7 과거 이력 기반 알고리즘의 구현</h1>
<ul>
<li>이처럼 LRU는 좋은 결과를 보이지만 과거 정보에 기반 둔 정책은 문제 있음</li>
<li>어떤 페이지 교체할지 알아내는 비용을 최소화 해야함</li>
<li>이를 위해 페이지 접근시 해당 페이지를 목록의 맨 앞으로 이동</li>
<li>최근 접근 을 관리하기 위함</li>
</ul>
<h1 id="228-lru-정책-근사하기">22.8 LRU 정책 근사하기</h1>
<p>LRU 정책에 가까운 구현은 가능하다</p>
<ul>
<li>use bit(reference bit)라는 하트웨어 지원이 필요</li>
<li>페이지 참조될 때 마다 HW에 의해 use bit가 1로 설정됨</li>
<li>HW는 이 비트를 절대 지우지 않음(0으로 설정 X)</li>
<li>운영체제가 0으로 바꿈</li>
</ul>
<p>use bit를 활용하는 방법은 시계 알고리즘이 있다</p>
<p>가정</p>
<ul>
<li><p>시스템의 모든 페이지들이 환형 리스트 구성</p>
</li>
<li><p>시계바늘이 특정 페이지 가리킴</p>
</li>
<li><p>페이지 교체 시 OS는 현재 바늘이 가르키고 있는 페이지 use bit가 1이면 (최근에 사용) 교체 대상 X</p>
</li>
<li><p>후 use bit 0으로 설정, 다음 페이지 넘어가 use bit가 0인 페이지 찾음</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/4a3295b0-182b-4fd5-8e12-c85c4e6f7859/image.png" alt=""></p>
<h1 id="229-갱신된-페이지-dirty-page의-고려">22.9 갱신된 페이지 (Dirty Page)의 고려</h1>
<p>페이지가 변경 되어 더티 상태 되었을 때 그 페이지를 내보내기 위해서는 디스크에 변경내용을 기록해야 하기 때문에 비용이 높다</p>
<p>변경되지 않았으면 추가비용 X</p>
<p>이 동작을 지원하기 위해 HW는 modified bit(더티 비트) 포함해야 함</p>
<h1 id="2211-쓰래싱">22.11 쓰래싱</h1>
<p>쓰래싱(thrashing): 메모리 사용 요구가 감당X, 실행중 프로세스가 요구하는 메모리가 가용 물리 메모리 크기를 초과하는 경우 시스템이 무한 페이징을 하는 상황</p>
<p>해결 방안</p>
<ul>
<li>진입 제어<ul>
<li>다수 프로세스 존재 시 일부 프로세스 실행 중지</li>
<li>워킹 셋: 프로세스가 실행중 일정 시간동안 사용하는 페이지 집합</li>
</ul>
</li>
<li>메모리 부족 킬러(일부 버전 Linux)<ul>
<li>메머리 요구 초과 시 실행</li>
<li>메모리 요구량 높은 프로세스 골라죽임</li>
<li>문제가 많다</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[OS : 20. 페이징 : 더 작은 테이블 ]]></title>
            <link>https://velog.io/@tech_n0/OS-20.-%ED%8E%98%EC%9D%B4%EC%A7%95-%EB%8D%94-%EC%9E%91%EC%9D%80-%ED%85%8C%EC%9D%B4%EB%B8%94</link>
            <guid>https://velog.io/@tech_n0/OS-20.-%ED%8E%98%EC%9D%B4%EC%A7%95-%EB%8D%94-%EC%9E%91%EC%9D%80-%ED%85%8C%EC%9D%B4%EB%B8%94</guid>
            <pubDate>Sun, 16 Feb 2025 11:30:39 GMT</pubDate>
            <description><![CDATA[<ul>
<li>페이징의 두번째 문제점</li>
</ul>
<p>페이지 테이블이 크면 많은 메모리 공간을 차지</p>
<p>ex)</p>
<ul>
<li>페이지 크기 4KB(2$^{12}$바이트)</li>
<li>주소공간 크기:  32 비트(2$^{32}$바이트)</li>
<li>가상 페이지 개수 = 주소공간 / 페이지 크기</li>
<li>2$^{32}$ / 2$^{12}$ = 2$^{20}$ (약 백만개)</li>
</ul>
<h1 id="211-간단한-해법--더-큰-페이지">21.1 간단한 해법 : 더 큰 페이지</h1>
<p>페이지 크기를 증가시키면 됨</p>
<p>위의 예시에서 페이지 크기를 16KB로 올리면</p>
<ul>
<li>페이지 크기 16KB(2$^{14}$ 바이트)</li>
<li>주소공간 크기:  32 비트(2$^{32}$ 바이트)</li>
<li>가상 페이지 개수: 2$^{18}$</li>
</ul>
<p>부작용</p>
<ul>
<li>내부단편화: 페이지 내부 낭비 공간 증가</li>
</ul>
<h1 id="232-하이브리드-접근-방법--페이징과-세그멘트">23.2 하이브리드 접근 방법 : 페이징과 세그멘트</h1>
<p>두개의 심장 어쩌고</p>
<p>페이징과 세그먼트의 장점을 합쳐서</p>
<p>ex) 1KB 크기 페이지를 같는 16KB 주소공간</p>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/ec9a1e5a-d593-402d-b840-75e52b06b5b2/image.png" alt=""></p>
<p>프로세스의 전체 주소 공간을 위해 하나의 페이지 테이블을 두는 대신, </p>
<p><strong>논리 세그멘트 마다 따로 페이지 테이블을 둠</strong></p>
<p>코드, 힙, 스택 세그멘트에 대하 페이지 테이블을 각각 두는것</p>
<p>베이스 바운드 리미트 레지스터가 있다.</p>
<p>여기서 베이스는 새그멘트 시작 주소가 아니라 세그멘트의 페이지 테이블의 시작 주소를 갖음</p>
<p>바운드는 페이지 테이블의 끝을 나타냄</p>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/f7037d62-714b-4696-92ec-e2e26785b2d5/image.png" alt=""></p>
<ul>
<li>소속 세그멘트를 나타내는 상위 두비트(세그멘트 구분)</li>
<li>TLB 미스 발생 시<ul>
<li>세그멘트 비트를 사용해 어떤 베이스와 바운드 쌍을 사용할지 결정</li>
<li>HW는 레지스터에 들어 있는 물리 주소를 VPN과 다음 과같은 형식으로 조작하여 페이지 테이블 항목(PTE)의 주소 얻 음</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/a0b38167-166f-4898-a5dc-39a1ca367044/image.png" alt=""></p>
<p>문제</p>
<ol>
<li>여전히 세그멘테이션을 사용</li>
<li>하이브리드 기법은 외부 단편화 유발(페이지 크기 제한 X, 다양한 크기를 갖음)</li>
</ol>
<h1 id="233-멀티-레벨-페이지-테이블">23.3 멀티 레벨 페이지 테이블</h1>
<p>세그멘테이션 사용X 페이지 테이블 크기 줄이는</p>
<p> 방법</p>
<p>멀티 레벨 페이지 테이블 에서는 선형 페이지 테이블을 트리구조로 표현</p>
<p>개념</p>
<ul>
<li>페이지 테이블을 페이지 크기의 단위로 나눔</li>
<li>페이지 테이블의 페이지가 유효하지 않은 항목만 있으면 해당 페이지 할당 X</li>
<li>페이지 디렉터리 자료구조 사용해 페이지 테이블 각 페이지의 할당 여부, 위치 파악<ul>
<li>페이지 디렉터리: 페이지 테이블을 구성하는 각 페이지의 존재 여부와 위치정보 갖고 있음</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/tech_n0/post/cdf06e91-4726-4fff-868b-eda902e6a71f/image.png" alt=""></p>
<ul>
<li>선형은 가운데 공간이 낭비중</li>
<li>페이지 디렉토리 사용시 미사용 페이지는 할당 X</li>
<li>페이지 디렉토리:  페이지 디렉토리 항목(PDE)으로 구성<ul>
<li>PDE는 페이지 테이블의 한 페이지</li>
<li>PDE는 유효 비트, PFN 으로 구성</li>
</ul>
</li>
</ul>
<p>장점</p>
<ol>
<li>사용된 주소 공간의 크기에 비례해 페이지 테이블 공간이 할당<ol>
<li>작은 페이지 테이블로 주소공간 표현 가능</li>
</ol>
</li>
<li>페이지 테이블을 페이지 크기로 분할해 메모리 관리가 용이<ol>
<li>패이지 테이블 할당/확장 시 OS가 free 페이지 풀에 있는 빈페이지 가져다 씀</li>
</ol>
</li>
</ol>
<p>단점</p>
<ol>
<li>추가 비용 발생<ol>
<li>TLB 미스 시 주소 변환을 위해 두번의 메모리 로드 발생(페이지 디렉터리, PTE 접근 각각 한번씩), 선형은 한번만 하면 됨</li>
</ol>
</li>
<li>복잡도<ol>
<li>선형 테이블 보다 테이블 검색이 복잡함</li>
</ol>
</li>
</ol>
<h1 id="234-역-페이지-테이블">23.4 역 페이지 테이블</h1>
<p>여러가지 페이지 테이블(시스템의 프로세스당 하나씩)  대신 시스템에  단 하나의 페이지 테이블만 둠</p>
<ul>
<li>페이지 테이블은 물리 페이지를 가상 주소 상의 페이지로 변환</li>
<li>역 페이지 테이블 각 항목은 해당 물리 페이지를 사용중인 프로세스 번호, 해당 가상 페이지 번호 가짐</li>
<li>탐색 속도 향상 위해 해시 테이블 사용</li>
</ul>
<h1 id="235-페이지-테이블을-디스크로-스와핑-하기">23.5 페이지 테이블을 디스크로 스와핑 하기</h1>
<p>모든 페이지 테이블을 메모리에 상주시키기에는 너무 클 수 있음</p>
<ul>
<li>어떤 시스템들은 페이지 테이블을 커널 가상 메모리에 존재 시킴</li>
<li>시스템 메모리가 부족할 경우 페이지 테이블들을 디스크로 스왑</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>