<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>h_rin.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Fri, 14 Nov 2025 00:47:51 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>h_rin.log</title>
            <url>https://velog.velcdn.com/images/h_rin/profile/1bdf239a-fd14-45da-8c42-41204d8bf47e/image.JPG</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. h_rin.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/h_rin" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[AI 논문 요약/정리]Efficient Methods for Natural Language Processing: A Survey(효율적인 자연어 처리 방법: 조사 연구)]]></title>
            <link>https://velog.io/@h_rin/AI-%EB%85%BC%EB%AC%B8-%EC%9A%94%EC%95%BD%EC%A0%95%EB%A6%ACEfficient-Methods-for-Natural-Language-Processing-A-Survey%ED%9A%A8%EC%9C%A8%EC%A0%81%EC%9D%B8-%EC%9E%90%EC%97%B0%EC%96%B4-%EC%B2%98%EB%A6%AC-%EB%B0%A9%EB%B2%95-%EC%A1%B0%EC%82%AC-%EC%97%B0%EA%B5%AC</link>
            <guid>https://velog.io/@h_rin/AI-%EB%85%BC%EB%AC%B8-%EC%9A%94%EC%95%BD%EC%A0%95%EB%A6%ACEfficient-Methods-for-Natural-Language-Processing-A-Survey%ED%9A%A8%EC%9C%A8%EC%A0%81%EC%9D%B8-%EC%9E%90%EC%97%B0%EC%96%B4-%EC%B2%98%EB%A6%AC-%EB%B0%A9%EB%B2%95-%EC%A1%B0%EC%82%AC-%EC%97%B0%EA%B5%AC</guid>
            <pubDate>Fri, 14 Nov 2025 00:47:51 GMT</pubDate>
            <description><![CDATA[<h2 id="1개요instroduction">1.개요(Instroduction)</h2>
<h4 id="배경-및-동기">배경 및 동기</h4>
<ul>
<li>최근 NLP 연구는 모델 파라미터와 훈련 데이터의 <strong>규모 확장(Scaling)</strong>을 통해 뛰어난 성능을 달성 </li>
</ul>
<p>→ 데이터, 시간, 저장 공간, 에너지 등의 자원 소비↑ </p>
<p>이러한 한계를 극복하고 리소스가 제한된 환경에서도 연구 성과에 접근 가능하도록 하기 위해, 더 적은 자원으로 유사한 결과를 얻는 <strong>효율적인 방법(efficient methods)</strong>에 대한 연구가 활발해지고 있습니다.</p>
<h4 id="효율성의-정의">효율성의 정의</h4>
<p>: 효율성은 시스템에 투입되는 자원과 그 출력 간의 관계로 특징지어지고 더 효율적인 시스템은 더 적은 자원으로 동일한 출력을 생성합니다.</p>
<h4 id="논문의-범위">논문의 범위</h4>
<ul>
<li>효율적인 NLP 방법을 체계적으로 정리 </li>
<li>리소스가 제한된 환경에서 NLP를 수행하는 연구자 와 효율적인 방법의 발전에 관심 있는 연구자 에게 지침을 제공하는 것을 목표</li>
</ul>
<p>순서: 데이터 효율성 (2) → 모델 설계 (3) → 사전 학습 (4) → 미세 조정 (5) → 추론 및 압축 (6) → 하드웨어 활용 (7) → 효율성 평가 (8) → 모델 선택 (9)</p>
<hr>

<h2 id="2-데이터-data">2. 데이터 (Data)</h2>
<p>데이터 효율성: 더 적은 훈련 인스턴스를 사용||사용 가능한 인스턴스를 더 잘 활용함 → 향상 </p>
<h3 id="21-필터링-filtering">2.1 필터링 (Filtering)</h3>
<p>: 데이터 품질을 향상시켜 사전 학습 및 미세 조정 중 훈련 비용 감소</p>
<h4 id="중복-제거-de-duplication">중복 제거 (De-duplication)</h4>
<p>: 사전 학습 데이터에서 중복을 제거하면 훈련 효율성이 증가 → 컴퓨팅 비용↓ 
(예: OPT에서 MinhashLSH 사용)</p>
<h4 id="적대적-필터링-adversarial-filtering">적대적 필터링 (Adversarial filtering)</h4>
<p>: 미세 조정에서도 작은 데이터셋으로 전체 데이터셋과 비슷한 성능 얻을 수 있음. (예: SNLI 데이터의 $\sim 2%$만 사용).</p>
<h3 id="22-능동-학습-active-learning">2.2 능동 학습 (Active Learning)</h3>
<p>: 데이터 수집 단계에서 가장 유용한 인스턴스만 주석을 달아 훈련 인스턴스의 수를 줄이는 것을 목표로 함.</p>
<h4 id="유용성-평가-기준">유용성 평가 기준</h4>
<p>: 모델 불확실성 (가장 불확실한 인스턴스에 레이블링) , 인스턴스 대표성 (다양성 극대화) , 이 둘 포함한 평가</p>
<h4 id="도전-과제">도전 과제</h4>
<p>: 모델 기반 샘플링이 다른 아키텍처 모델 성능에 미치는 영향 불분명 , &#39;어려운&#39; 인스턴스 선택 시 주석 비용 증가 , 선택 편향 및 이상치 선호 위험.</p>
<h3 id="23-커리큘럼-학습-curriculum-learning">2.3 커리큘럼 학습 (Curriculum Learning)</h3>
<p>: 목표 성능 달성에 필요한 훈련 단계 수를 줄이는 데이터 순서를 찾는 것을 목표로 함.</p>
<ul>
<li>방법: 인스턴스를 난이도 순서로 정렬 (예: 문장 길이 사용).</li>
</ul>
<h4 id="적응형-전략">적응형 전략</h4>
<p>: 현재 모델 상태에 기반하여 속도를 조절하는 자기 주도 학습(self-paced learning) (예: MT에서 모델 및 데이터 불확실성 사용)</p>
<h4 id="도전-과제-1">도전 과제</h4>
<p>: 난이도 진행 속도를 신중하게 선택해야 함 , 자기 주도 학습은 높은 훈련 비용 발생.</p>
<hr>

<h2 id="3-모델-설계-model-design">3. 모델 설계 (Model Design)</h2>
<p>효율적인 모델 설계는 아키텍처 변경과 훈련 가속화를 위한 새로운 모듈 추가를 포함.</p>
<h3 id="31-트랜스포머의-어텐션-개선-improving-attention-in-transformers">3.1 트랜스포머의 어텐션 개선 (Improving Attention in Transformers)</h3>
<p>: 트랜스포머의 자기-어텐션 메커니즘은 시퀀스 길이에 대해 2차(Quadratic) 의존성을 가지므로, 이를 줄이는 방법을 연구함.</p>
<p>*2차 의존성 - 한 변수의 변화가 다른 변수에 제곱에 비례하여 영향을 미치는 관계</p>
<h4 id="장거리-시퀀스-처리">장거리 시퀀스 처리</h4>
<p>: 순환(recurrence)을 통한 세그먼트 연결 (Transformer-XL) , 압축된 장기 메모리 학습 (Compressive Transformers) , 고정된 어텐션 패턴 (Longformer, Big Bird) , 저랭크 근사(low-rank approximations)를 통한 선형 시간 어텐션 (Performer)</p>
<h4 id="대안-아키텍처">대안 아키텍처</h4>
<p>: S4 및 Mega와 같은 상태 공간 표현(state space representations) 기반 모델은 셀프-어텐션의 2차 병목 현상을 완화하고 긴 시퀀스에서 트랜스포머 기반 방법을 능가합니다.</p>
<p>*자기-어텐션 - 자기 자신을 취한다
*어텐션 메커니즘 - 딥 러닝 모델이 입력 데이터에서 가장 관련성이 높은 부분에 우선순위를 부여(또는 주의)하도록 지시하는 머신 러닝 기법</p>
<h3 id="32-희소-모델링-sparse-modeling">3.2 희소 모델링 (Sparse Modeling)</h3>
<p>: 계산을 전체 모델 대신 작은 서브네트워크로 라우팅하는 전문가 혼합(Mixture-of-Experts, MoE) 개념을 활용합니다.</p>
<p>예: Switch Transformer.</p>
<p>장점: 여러 NLP 태스크에서 강력한 성능을 달성하면서 전체 자원 소비를 줄입니다 (예: GLaM은 GPT-3 에너지 소비의 ∼ 1/3 , DeepSpeed-MoE는 훈련 비용 5배 감소).</p>
<p>도전 과제: 훈련 불안정성, 아키텍처별 구현 필요.</p>
<h3 id="33-파라미터-효율성-parameter-efficiency">3.3 파라미터 효율성 (Parameter Efficiency)</h3>
<p>: 파라미터 수를 줄여 계산 비용과 메모리 사용량을 감소함</p>
<h4 id="가중치-공유">가중치 공유</h4>
<p>: 모델 레이어 간에 가중치를 공유함 (ALBERT, Universal Transformers)</p>
<h4 id="잠재-벡터-매핑">잠재 벡터 매핑</h4>
<p>: Perceiver는 입력을 작은 잠재 벡터로 매핑하여 자기-어텐션의 계산 비용을 최소화함</p>
<h3 id="34-검색-증강-모델-retrieval-augmented-models">3.4 검색 증강 모델 (Retrieval-Augmented Models)</h3>
<p>: 파라미터 모델을 검색 메커니즘과 결합하여 텍스트를 생성하는 준파라미터 모델</p>
<h4 id="작동-방식">작동 방식</h4>
<p>: 모델 크기를 데이터베이스 항목 수와 교환하며 , 추론 시 데이터베이스에서 토큰/구문/문장을 검색하여 모델이 활용함</p>
<p>예: RETRO는 25배 큰 모델과 비슷한 성능을 달성하며 , 도메인별 미세 조정의 필요성을 줄임</p>
<h4 id="도전-과제-2">도전 과제</h4>
<p>: 검색 시간이 데이터 저장소 규모에 따라 증가할 수 있음.</p>
<hr>

<h2 id="4-사전-학습-pre-training">4. 사전 학습 (Pre-training)</h2>
<p>사전 학습 절차를 개선하면 하이퍼파라미터 튜닝 비용을 크게 줄이고 미세 조정의 데이터 효율성을 높일 수 있습니다.</p>
<h3 id="41-최적화-목표-optimization-objective">4.1 최적화 목표 (Optimization Objective)</h3>
<ul>
<li>Causal Language Modeling (CLM)1
: GPT, PaLM 등 (다음 토큰 예측)</li>
<li>Masked Language Model (MLM)
: BERT 등 (랜덤 마스킹된 토큰 채우기)</li>
<li>Replaced Token Detection (RTD)
: ELECTRA 등 (작은 생성 모델이 대체한 토큰 감지)</li>
<li>Denoising Sequence-to-Sequence
: T5, BART 등 (마스킹된 위치에 대한 토큰 스팬 예측, 인코더-디코더 모델 사전 학습). 훈련 시퀀스 길이를 줄여 훈련 비용 절감에 도움.</li>
</ul>
<h3 id="42-사전-학습-고려-사항">4.2 사전 학습 고려 사항</h3>
<h4 id="모델-크기와-데이터의-균형">모델 크기와 데이터의 균형</h4>
<p>: Chinchilla의 연구에 따르면, 사용 가능한 데이터의 양을 고려하여 모델 크기를 줄이는 것이 성능 향상 및 계산 비용 감소</p>
<h4 id="효율적인-모델링-방법">효율적인 모델링 방법</h4>
<p>: 트랜스포머 대신 상태 공간 표현 및 MoE가 사전 학습의 일부 과제를 극복할 잠재력 있음.</p>
<hr>

<h2 id="5-미세-조정-fine-tuning">5. 미세 조정 (Fine-tuning)</h2>
<p>미세 조정은 사전 학습된 모델을 새로운 다운스트림 작업에 맞게 조정하는 과정</p>
<h3 id="51-파라미터-효율적인-미세-조정-parameter-efficient-fine-tuning-peft">5.1 파라미터 효율적인 미세 조정 (Parameter-Efficient Fine-Tuning, PEFT)</h3>
<p>: 전체 모델 미세 조정보다 훨씬 적은 수의 파라미터만 업데이트하거나 추가</p>
<h4 id="어댑터-adapters">어댑터 (Adapters)</h4>
<p>: 사전 학습된 모델에 새로운 학습 가능한 밀집 레이어를 삽입하고, 기존 파라미터는 고정. 
단점 - 파라미터 증가로 인해 추론 시간이 늘어날 수 있음.</p>
<h4 id="활성화-수정">활성화 수정</h4>
<p>: 학습된 벡터를 연결(Prefix-tuning, Prompt-tuning)하거나 곱하거나 더하여 활성화를 직접 수정</p>
<h4 id="희소저랭크-업데이트">희소/저랭크 업데이트</h4>
<p>: 새로운 파라미터를 추가하지 않고 희소 업데이트 (Diff Pruning) 또는 저랭크 업데이트 (LoRA)를 수행</p>
<h3 id="52-다중-작업-및-제로샷-학습-multi-task-and-zero-shot-learning">5.2 다중 작업 및 제로샷 학습 (Multi-Task and Zero-Shot Learning)</h3>
<h4 id="다중-작업-학습">다중 작업 학습</h4>
<p>: 단일 모델을 여러 다운스트림 작업 데이터로 미세 조정하여 다양한 작업을 수행할 수 있도록 함.</p>
<h4 id="제로샷-일반화">제로샷 일반화</h4>
<p>: 모델이 미세 조정 없이 새로운 작업에서 작동하는 것을 의미하며, 모델 크기에 따라 경쟁력이 생김.</p>
<h3 id="53-프롬프팅-prompting">5.3 프롬프팅 (Prompting)</h3>
<p>: 작업을 언어 모델에 대한 텍스트 지침으로 구성하는 것을 말하며, 미세 조정의 필요성을 없앨 수 있음.</p>
<h3 id="54-미세-조정-고려-사항">5.4 미세 조정 고려 사항</h3>
<p>: PEFT 방법을 결합하거나, 작업별 어댑터를 사용하여 프롬프트 생성을 피하는 등의 접근 방식이 효율적인 새로운 지식 도입을 위한 유망한 방향.</p>
<hr>

<h2 id="6-추론-및-압축-inference-and-compression">6. 추론 및 압축 (Inference and Compression)</h2>
<p>추론 효율성은 시간 효율성(지연 시간)을 위해 프로세스를 가속화하거나 메모리 요구 사항을 줄이기 위해 모델을 압축함으로써 향상될 수 있습니다.</p>
<h3 id="61-가지치기-pruning">6.1 가지치기 (Pruning)</h3>
<p>: 계산을 줄이고 메모리 용량 요구 사항을 낮추기 위해 신경망에서 불필요한 가중치를 제거합니다.</p>
<h4 id="종류">종류</h4>
<ul>
<li>개별 가중치 수준의 비구조적 가지치기(unstructured pruning)</li>
<li>더 큰 구성 요소(예: 어텐션 헤드, 레이어)를 제거하는 구조적 가지치기(structured pruning). 
  → 구조적 가지치기는 추론 속도 개선에 더 큰 향상을 가져옵니다.</li>
</ul>
<h3 id="62-지식-증류-knowledge-distillation">6.2 지식 증류 (Knowledge Distillation)</h3>
<p>: 대규모(교사) 모델의 지도 신호를 사용하여 더 작은(학생) 모델을 훈련시키는 과정입니다.</p>
<p>예: TinyBERT, MobileBERT.</p>
<h3 id="63-양자화-quantization">6.3 양자화 (Quantization)</h3>
<p>: 고정밀 데이터 유형을 저정밀 데이터 유형으로 매핑하여 메모리 소비와 훈련/추론 비용을 줄입니다.</p>
<p>→ 8bit, 3진법,2진 표현까지 연구</p>
<h4 id="혼합-정밀도-양자화-mixed-precision-quantization">혼합 정밀도 양자화 (Mixed-precision quantization)</h4>
<p>: 구성 요소별로 다른 정밀도 민감도를 고려하여 적용합니다.</p>
<h3 id="64-추론-고려-사항">6.4 추론 고려 사항</h3>
<h4 id="동적-계산-dynamic-computation">동적 계산 (Dynamic computation)</h4>
<p>: 입력에 필요한 부분에만 선택적으로 계산을 수행하여 효율성을 개선합니다. (예: early-exit, MoE)</p>
<h4 id="최적화의-맥락-의존성">최적화의 맥락 의존성</h4>
<p>: 추론 최적화는 사용 사례에 따라 요구 사항이 다르므로, 단일 최적화 솔루션은 없습니다.</p>
<hr>

<h2 id="7-하드웨어-활용-hardware-utilization">7. 하드웨어 활용 (Hardware Utilization)</h2>
<p>주로 GPU 메모리 소비를 줄이는 데 중점을 둡니다.</p>
<h3 id="71-옵티마이저-메모리-감소">7.1 옵티마이저 메모리 감소</h3>
<ul>
<li>DeepSpeed 라이브러리는 그래디언트 기록을 GPU에서 CPU RAM으로 오프로드합니다. </li>
<li>bitsandbytes는 블록별 양자화를 사용하여 메모리 압력을 줄입니다.</li>
</ul>
<h3 id="72-특수-하드웨어-specialized-hardware">7.2 특수 하드웨어 (Specialized Hardware)</h3>
<p>: ASIC 또는 FPGA를 사용하여 양자화 및 가지치기와 같은 효율적인 작업을 위한 전용 유닛을 구축합니다.</p>
<h3 id="73-공동-설계-co-design">7.3 공동 설계 (Co-design)</h3>
<p>: 하드웨어, 소프트웨어, 알고리즘을 함께 최적화하여 효율성 향상을 실현합니다. (예: 컴파일러 개선, 하드웨어 인식 MoE)</p>
<h3 id="74-엣지-디바이스-edge-devices">7.4 엣지 디바이스 (Edge Devices)</h3>
<p>: 엄격한 컴퓨팅 및 메모리 제약이 있는 엣지 디바이스를 위한 솔루션입니다. (예: SqueezeBERT, GhostBERT, ProFormer)</p>
<hr>

<h2 id="8-효율성-평가-evaluating-efficiency">8. 효율성 평가 (Evaluating Efficiency)</h2>
<p>효율성을 평가하려면 최소화하려는 계산 측면을 설정해야 합니다.</p>
<h3 id="81-평가-측정-기준">8.1 평가 측정 기준</h3>
<h4 id="파레토-최적성-pareto-optimality">파레토 최적성 (Pareto Optimality)</h4>
<p>: 작업 성능과 자원 소비 간의 절충점을 특성화하는 데 사용되며, 주어진 문제와 측정 공간에서 파레토 최적 곡선에 기여하는 모델은 가치가 있습니다.</p>
<h4 id="flops-floating-point-operations-per-second">FLOP/s (Floating Point Operations per second)</h4>
<p>: 하드웨어가 수행하는 작업 측면에서 잘 정의된 것처럼 보이지만, 하드웨어별 변동성, 비-부동 소수점 작업 미고려, 낮은 하드웨어 활용률 등으로 불확실성이 있습니다.</p>
<h4 id="전력-소비-power-consumption">전력 소비 (Power Consumption)</h4>
<p>: 전기 계량기 또는 MLCO2와 같은 소프트웨어 도구를 사용하여 측정할 수 있으며, 냉각 및 네트워킹과 같은 외부 에너지 비용은 포함하지 않습니다.</p>
<h4 id="탄소-배출량-carbon-emissions">탄소 배출량 (Carbon Emissions)</h4>
<p>: 전력 소비와 한계 에너지 생성의 탄소 집약도를 사용하여 계산됩니다.</p>
<h3 id="82-효율성-측정의-도전-과제">8.2 효율성 측정의 도전 과제</h3>
<h4 id="단계-분리">단계 분리</h4>
<p>: 사전 학습 및 미세 조정 단계의 효율성을 분리하여 특성화하는 것이 중요합니다.</p>
<h4 id="비용-요인-간의-불일치">비용 요인 간의 불일치</h4>
<p>: MoE는 파라미터 수를 늘리지만 FLOPs는 줄이는 등, 비용 지표들이 서로 상충될 수 있습니다.</p>
<h4 id="다른-요구-사항과의-절충">다른 요구 사항과의 절충</h4>
<p>: 효율성 개선은 공정성(fairness)과 견고성(robustness)과 같은 다른 요구 사항과 상충될 수 있습니다. (예: 압축 기술이 기존 편향을 증폭시킬 수 있음)</p>
<hr>

<h2 id="9-모델-선택-model-selection">9. 모델 선택 (Model Selection)</h2>
<p>성능이 좋은 모델 변형을 효율적으로 선택하는 연구를 다룹니다.</p>
<h3 id="91-하이퍼파라미터-검색-hyperparameter-search">9.1 하이퍼파라미터 검색 (Hyperparameter Search)</h3>
<ul>
<li>모델 기반 기술
: 베이지안 최적화(BO)</li>
<li>병렬 기술
: 연속적 절반 감소(SHA) 및 비동기 SHA(ASHA)는 여러 설정을 테스트하고 성능이 나쁜 설정을 제거합니다.</li>
</ul>
<h3 id="92-하이퍼파라미터-전이-hyperparameter-transfer">9.2 하이퍼파라미터 전이 (Hyperparameter Transfer)</h3>
<p>: 최적의 하이퍼파라미터 설정을 찾기 위한 시도 횟수를 최소화하기 위해 다른 데이터셋이나 작업의 지식을 전이합니다.</p>
<hr>

<h2 id="10-결론-conclusion">10. 결론 (Conclusion)</h2>
<h4 id="주요-성과">주요 성과</h4>
<p>: 대부분의 발전은 특정 계산 예산 및 하드웨어 패러다임을 목표로 하는 모델 설계에서 이루어졌습니다.</p>
<h4 id="남은-도전-과제">남은 도전 과제</h4>
<p>: 엔드-투-엔드 작업 성능과 자원 소비 간의 절충점을 더 잘 이해하고 모델링하는 것, 그리고 하드웨어 선택과 소프트웨어 구현 간의 의존성을 해결하는 것이 포함됩니다.</p>
<h4 id="효율성의-다면성">효율성의 다면성</h4>
<p>: NLP의 효율성은 여러 정의를 가지며 단일 메트릭으로 측정될 수 없습니다.</p>
<h4 id="향후-방향">향후 방향</h4>
<p>: 사용 가능한 데이터의 더 나은 활용, 대규모 모델의 사전 학습 및 미세 조정 비용 절감, 알고리즘-소프트웨어-하드웨어 간 상호 작용의 중요성 우선순위 지정 등이 유망한 연구 방향으로 제시됩니다.</p>
<br>

]]></description>
        </item>
        <item>
            <title><![CDATA[[혼자 공부하는 머신러닝 딥러닝]Chapter 09. 텍스트를 위한 인공 신경망]]></title>
            <link>https://velog.io/@h_rin/%ED%98%BC%EC%9E%90-%EA%B3%B5%EB%B6%80%ED%95%98%EB%8A%94-%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-%EB%94%A5%EB%9F%AC%EB%8B%9DChapter-09.-%ED%85%8D%EC%8A%A4%ED%8A%B8%EB%A5%BC-%EC%9C%84%ED%95%9C-%EC%9D%B8%EA%B3%B5-%EC%8B%A0%EA%B2%BD%EB%A7%9D</link>
            <guid>https://velog.io/@h_rin/%ED%98%BC%EC%9E%90-%EA%B3%B5%EB%B6%80%ED%95%98%EB%8A%94-%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-%EB%94%A5%EB%9F%AC%EB%8B%9DChapter-09.-%ED%85%8D%EC%8A%A4%ED%8A%B8%EB%A5%BC-%EC%9C%84%ED%95%9C-%EC%9D%B8%EA%B3%B5-%EC%8B%A0%EA%B2%BD%EB%A7%9D</guid>
            <pubDate>Mon, 06 Jan 2025 06:26:21 GMT</pubDate>
            <description><![CDATA[<h1 id="09-2-순환-신경망으로-imdb-리뷰-분류하기">09-2 순환 신경망으로 IMDB 리뷰 분류하기</h1>
<h2 id="키워드">키워드</h2>
<ul>
<li><p>말뭉치
: 자연어 처리에서 사용하는 텍스트 데이터의 모음, 훈련 데이터셋</p>
</li>
<li><p>토큰
: 텍스트에서 공백으로 구분되는 문자열, 종종 소문자로 변환하고 구둣점은 삭제함</p>
</li>
<li><p>원-핫 인코딩
: 어떤 클래스에 해당하는 원소만 1이고 나머지는 모두 0인 벡터, 정수로 변환된 토큰을 원-핫 인코딩으로 변환하려면 어휘 사전 크기의 벡터가 필요함</p>
</li>
<li><p>단어 임베딩
: 정수로 변환된 토큰을 비교적 작은 크기의 실수 밀집 벡터로 변환, 이 밀집 벡터는 단어 사이의 관계를 표현할 수 있기 때문에 자연어 처리에서 좋은 성능을 발휘</p>
</li>
</ul>
<h2 id="imdb-리뷰-데이터셋">IMDB 리뷰 데이터셋</h2>
<ul>
<li>텍스트 자체를 신경망에  전달하는 것은 아님, 텍스트 데이터의 단어를 숫자 데이터로 바꾸는 일반적인 방법은 데이터에 나오는 단어마다 고유한 정수를 부여하는 것
ex) He -&gt; 10 / follows -&gt; 11 / the -&gt; 12 ... He -&gt; 10 / loves -&gt; 13 ...
위 예시와 같이 단어들을 정수에 매핑하고 동일한 단어는 동일한 정수에 매핑함</li>
</ul>
<p>일반적으로 영어 문장은 모두 소문자로 바꾸고 구둣점을 삭제한 다음 공백을 기준으로 분리함 -&gt; 토큰(token), 하나의 샘플은 여러개의 토큰으로 이루어짐, 1개의 토큰이 하나의 타임스텝에 해당</p>
<pre><code># 실행마다 동일한 결과를 얻기 위해 케라스에 랜덤 시드를 사용하고 텐서플로 연산을 결정적으로 만듭니다.
import tensorflow as tf

tf.keras.utils.set_random_seed(42)

from tensorflow.keras.datasets import imdb

(train_input, train_target), (test_input, test_target) = imdb.load_data(
    num_words=200)

print(train_input.shape, test_input.shape)

-&gt; (25000,) (25000,)</code></pre><p>위 코드의 결과값을 보면 1차원 배열인 것을 알 수 있음, IMDB 리뷰 텍스트의 길이는 제각각이기 때문에 크기가 고정되어 있는 2차원 배열보다 이뷰마다 별도의 파이썬 리스트에 담아야 메모리를 효율적으로 사용가능함.</p>
<p><img src="https://velog.velcdn.com/images/h_rin/post/aa6f8dca-e659-4a43-b82d-3989b5cbd7a1/image.png" alt="">
개별 리뷰를 담은 파이썬 리스트 객체로 이루어진 넘파이 배열임</p>
<pre><code>print(len(train_input[0]))

-&gt; 
218 # 218개 토큰

print(len(train_input[1]))

-&gt; 
189 # 189개 토큰</code></pre><p>하나의 리뷰 == 하나의 샘플</p>
<p><strong>[첫번째 리뷰에 담긴 내용]</strong></p>
<ul>
<li>앞서 num_words=500으로 지정 -&gt; 어휘 사전에는 500개의 단어만 들어가 있음</li>
<li>어휘사전에 없는 단어는 모두 2로 표시됨<pre><code>print(train_input[0])
</code></pre></li>
</ul>
<p>-&gt;
[1, 14, 22, 16, 43, 2, 2, 2, 2, 65, 2, 2, 66, 2, 4, 173, 36, 2, 5, 
25, 100, 43, 2, 112, 50, 2, 2, 9, 35, 2, 2, 5, 150, 4, 172, 112, 167,
2, 2, 2, 39, 4, 172, 2, 2, 17, 2, 38, 13, 2, 4, 192, 50, 16, 6, 147, 
2, 19, 14, 22, 4, 2, 2, 2, 4, 22, 71, 87, 12, 16, 43, 2, 38, 76, 15, 
13, 2, 4, 22, 17, 2, 17, 12, 16, 2, 18, 2, 5, 62, 2, 12, 8, 2, 8, 
106, 5, 4, 2, 2, 16, 2, 66, 2, 33, 4, 130, 12, 16, 38, 2, 5, 25, 124, 
51, 36, 135, 48, 25, 2, 33, 6, 22, 12, 2, 28, 77, 52, 5, 14, 2, 16, 
82, 2, 8, 4, 107, 117, 2, 15, 2, 4, 2, 7, 2, 5, 2, 36, 71, 43, 2, 2, 
26, 2, 2, 46, 7, 4, 2, 2, 13, 104, 88, 4, 2, 15, 2, 98, 32, 2, 56, 
26, 141, 6, 194, 2, 18, 4, 2, 22, 21, 134, 2, 26, 2, 5, 144, 30, 2, 
18, 51, 36, 28, 2, 92, 25, 104, 4, 2, 65, 16, 38, 2, 88, 12, 16, 2, 
5, 16, 2, 113, 103, 32, 15, 16, 2, 19, 178, 32]</p>
<pre><code>
[타깃 데이터 출력]
- 리뷰가 긍정(1)인지 부정(0)인지 나뉨</code></pre><p>print(train_target[:20])</p>
<p>-&gt;
[1 0 0 1 0 0 1 0 1 0 1 0 0 0 0 0 1 1 0 1]</p>
<pre><code>
</code></pre><p>from sklearn.model_selection import train_test_split</p>
<p>train_input, val_input, train_target, val_target = train_test_split(
    train_input, train_target, test_size=0.2, random_state=42)</p>
<h1 id="훈련세트-8-검즘세트-2로-훈련세트-나눔">훈련세트 8: 검즘세트 2로 훈련세트 나눔</h1>
<pre><code>&lt;br&gt;

- 리뷰 길이의 평균값과 중간값 구함</code></pre><p>import numpy as np</p>
<p>lengths = np.array([len(x) for x in train_input])</p>
<p>print(np.mean(lengths), np.median(lengths))</p>
<p>-&gt;
239.00925 178.0</p>
<pre><code>
- 데이터가 한쪽으로 치우쳐져 있는 것을 알 수 있음
- 평균이 중간값보다 높기 때문에 아주 큰 데이터가 존재한다는 것을 유추 가능
- 리뷰 길이를 맞추기 위해 패딩을 사용 -&gt; 패딩을 나타내는 토큰으로 0 사용
- 수동으로 훈련세트에 있는 리뷰를 순회하면서 길이가 100이 되도록 잘라내거나 0으로 패딩할 수 있지만 불편함</code></pre><p>import matplotlib.pyplot as plt</p>
<p>plt.hist(lengths)
plt.xlabel(&#39;length&#39;)
plt.ylabel(&#39;frequency&#39;)
plt.show()</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/8898d1e1-33e7-4de3-a73d-45c6e041e18c/image.png)

- 케라스는 시퀀스 데이터의 길이를 맞추는 pad_swquences()함수 제공
- train_input의 길이를 100으로 맞춤
- maxlen을 지정 -&gt; 긴 경우는 잘라냄, 짧은 경우 0으로 패딩
</code></pre><p>from tensorflow.keras.preprocessing.sequence import pad_sequences</p>
<p>train_seq = pad_sequences(train_input, maxlen=100)</p>
<p>print(train_seq.shape)</p>
<p>-&gt;
(20000, 100)</p>
<pre><code>
- 앞 부분에 0이 있음 즉, 이 샘플의 길이는 100이 안됨을 의미함
- 패딩 토큰은 뒷부분이 아니라 앞부분에 추가됨, 시퀀그의 마지막에 있는 단어가 셀의 은닉 상태레 가장 큰 영향을 미치게 되므로 마지막에 패딩을 추가하는 것을 일반적으로 선호하지 않음
- pad_sequences()함수의 padding 매개변수의 기본값을 &#39;pre&#39;에서 &#39;post&#39;로 바꾸면 뒷부분에 패딩 추가하는 것으로 변경 가능
</code></pre><p>print(train_seq[5])</p>
<p>-&gt;
[  0   0   0   0   1   2 195  19  49   2   2 190   4   2   2   2 183  10
  10  13  82  79   4   2  36  71   2   8   2  25  19  49   7   4   2   2
   2   2   2  10  10  48  25  40   2  11   2   2  40   2   2   5   4   2
   2  95  14   2  56 129   2  10  10  21   2  94   2   2   2   2  11 190
  24   2   2   7  94   2   2  10  10  87   2  34  49   2   7   2   2   2
   2   2   2   2  46  48  64  18   4   2]</p>
<pre><code>

## 순환 신경망 만들기
- Dense나 Conv2D 클래스 대신 SimpleRNN 클래스 사용
- 정수값에 있는 크기 속성을 없애고 각 정수를 고유하게 표현하는 방법인 원-핫 인코딩으로 값을 바꾸면 정수값을 해당 정수 위치의 원소만1이고 나머지는 모두 0으로 변환함
- imdb.load_data() 함수에서 500개의 단어만 사용하도록 지정했기 때문에 고유한 단어는 모두 500개(0-499), 원-핫 인코딩으로 표현하려면 배열의 길이가 500
- 정수 하나마다 모두 500차원의 배열로 변경되었기 때문에 (20000, 100) 크기에서 (20000, 100, 500)으로 바뀜</code></pre><p>from tensorflow import keras</p>
<p>model = keras.Sequential()</p>
<p>model.add(keras.layers.SimpleRNN(8, input_shape=(100, 500)))
model.add(keras.layers.Dense(1, activation=&#39;sigmoid&#39;))</p>
<h1 id="원-핫-인코딩으로-변환">원-핫 인코딩으로 변환</h1>
<p>val_oh = keras.utils.to_categorical(val_seq)
train_oh = keras.utils.to_categorical(train_seq)
print(train_oh.shape)</p>
<p>-&gt;
(20000, 100, 500)</p>
<pre><code>
모델 구조 출력</code></pre><p>#책에서는 500으로 설정, 코랩 코드 내에서는 200으로 설정해서 책하고 param 값 다름
model.summary()</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/446799fc-1d9d-469d-8c24-06f58af0aeef/image.png)

모델의 파라미터는 입력 토큰 500개 x 순환층 뉴런 8개 + 은닉상태 크기 8개 x 뉴런 8개 + 절편 8개 = 4072개

Dense층의 가중치 개수 9개까지 더해서 총 4081개가 됨


## 순환 신경망 훈련하기
RMSprop 가 default 이지만 학습률을 0.0001,
patience 는 3 으로 설정하였고 조기종료하여 최적 모델을 저장하도록 설정
epoch 횟수 100으로 늘리고 배치 크기는 64로 설정
</code></pre><p>rmsprop = keras.optimizers.RMSprop(learning_rate=1e-4)
model.compile(optimizer=rmsprop, loss=&#39;binary_crossentropy&#39;,
              metrics=[&#39;accuracy&#39;])</p>
<p>checkpoint_cb = keras.callbacks.ModelCheckpoint(&#39;best-simplernn-model.keras&#39;,
                                                save_best_only=True)
early_stopping_cb = keras.callbacks.EarlyStopping(patience=3,
                                                  restore_best_weights=True)</p>
<p>history = model.fit(train_oh, train_target, epochs=100, batch_size=64,
                    validation_data=(val_oh, val_target),
                    callbacks=[checkpoint_cb, early_stopping_cb])</p>
<p>plt.plot(history.history[&#39;loss&#39;])
plt.plot(history.history[&#39;val_loss&#39;])
plt.xlabel(&#39;epoch&#39;)
plt.ylabel(&#39;loss&#39;)
plt.legend([&#39;train&#39;, &#39;val&#39;])
plt.show()</p>
<pre><code>
![](https://velog.velcdn.com/images/h_rin/post/17783da0-74b5-4a3e-8273-e15eda59f35d/image.png)
중간에 한번 튀는데 뭐지...
여튼 80번째쯤 가서 에포크 감소가 둔해지는 것으로 보아 에포크 훈련이 83번째에서 훈련을 멈췄다고 볼 수 있다.

&gt; keras.utils.to_categorical() 을 사용하여 원-핫 인코딩을 사용해 500개 중 하나만 1이고 나머지를 0으로 만들어서 크기 속성을 없애주었습니다. &lt;br&gt;
하지만 토큰 1개를 500차원으로 늘렸기 때문에 데이터가 엄청 커짐 &lt;br&gt;
실제로 원-핫 인코딩 방식은 단어의 의미나 단어 사이의 관계를 전혀 고려하지 않고 만약 단어를 2000개로 늘리면 더욱 크기가 늘어나게 된다

## 단어 임베딩 사용하기

: 각 단어를 고정된 크기의 실수 벡터로 바꿔줌

단어를 고정된 크기의 실수 벡터로 바꾸어주는 방법인데 단어의 의미를 고려하여 벡터로 표현한 것이다. 예를 들어, [고양이, 강아지, 개구리] 가 있다면 각각을 벡터로 표현하지만 고양이와 강아지 사이의 거리가 개구리와의 거리보다 가깝습니다. 고양이와 강아지는 포유류이고 개구리는 양서류이기 때문이다
[참고](https://velog.io/@boing-86/%ED%98%BC%EA%B3%B5%EB%A8%B8%EC%8B%A0-Chapter9-%ED%85%8D%EC%8A%A4%ED%8A%B8%EB%A5%BC-%EC%9C%84%ED%95%9C-%EC%9D%B8%EA%B3%B5%EC%8B%A0%EA%B2%BD%EB%A7%9D)

![](https://velog.velcdn.com/images/h_rin/post/60623533-51a6-4521-8d7b-6588d5922bae/image.png)
- 원-핫 인코딩된 벡터보다 의미 있는 값으로 채워져 있기 때문에 자연어 처리에서 더 좋은 성능을 내는 경우가 많음

- 단어 임베딩의 장점은 입력으로 정수 데이터를 받는 다는 것 
즉, 원-핫 인코딩으로 변경된 train_oh가 아니라 train_seq를 사용할 수 있음 때문에 메모리를 효율적으로 사용가능

- 앞서 원-핫 인코딩은 샘플 하나를 500차원으로 늘렸기 때문에 (100, )크기의 샘플이 (100, 500)으로 커졌음.
- 이와 비슷하게 임베딩도 (100, )크기의 샘플을 (100, 20)과 같이 2차원 배열로 늘림, 원-핫 인코딩과 다르게 훨씬 작은 크기로도 단어를 표현할 수 있음
</code></pre><p>model2 = keras.Sequential()</p>
<h1 id="500--어휘-사전-크기-16--임베팅-벡터-크기">500 =&gt; 어휘 사전 크기, 16 =&gt; 임베팅 벡터 크기</h1>
<p>model2.add(keras.layers.Embedding(500, 16, input_shape=(100,)))
model2.add(keras.layers.SimpleRNN(8))
model2.add(keras.layers.Dense(1, activation=&#39;sigmoid&#39;))</p>
<p>model2.summary()</p>
<pre><code>input_shape=(100,) 이 부분은 책에서 input_lenght=100으로 나오는데 실습 코드에서는 shape으로 사용한다. 이유는?

여튼 앞서 샘플 길이를 100으로 맞추었기 때문에 100으로 나타냄

![](https://velog.velcdn.com/images/h_rin/post/93b0ceae-711f-4fcf-ae9a-545572ba0b65/image.png)

500개 토큰을 크기 16 벡터로 변경하였기 때문에 500x16 크기의 모델 파라미터를 가진다. 뉴런이 8개 이기 때문에 16x8 개, 은닉상태의 가중치 8x8 + 8개 절편 -&gt; 8200개 모델파라미터가 있고 Dense층 9개이므로 8209개


![](https://velog.velcdn.com/images/h_rin/post/1149ca8f-1ab8-49cd-86c2-e29e64bb895a/image.png)

![](https://velog.velcdn.com/images/h_rin/post/ed92b590-4cc2-4c00-8e4a-1c172f754b7c/image.png)
validation loss가 Epoch 27/100 에서 제일 낫기 때문에 조기종료
train loss 는 계속 줄어드는 것을 확인 가능

[위 내용 참고](https://velog.io/@boing-86/%ED%98%BC%EA%B3%B5%EB%A8%B8%EC%8B%A0-Chapter9-%ED%85%8D%EC%8A%A4%ED%8A%B8%EB%A5%BC-%EC%9C%84%ED%95%9C-%EC%9D%B8%EA%B3%B5%EC%8B%A0%EA%B2%BD%EB%A7%9D)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[혼자 공부하는 머신러닝 딥러닝]Chapter 08. 이미지를 위한 인공 신경망]]></title>
            <link>https://velog.io/@h_rin/%ED%98%BC%EC%9E%90-%EA%B3%B5%EB%B6%80%ED%95%98%EB%8A%94-%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-%EB%94%A5%EB%9F%AC%EB%8B%9DChapter-08.-%EC%9D%B4%EB%AF%B8%EC%A7%80%EB%A5%BC-%EC%9C%84%ED%95%9C-%EC%9D%B8%EA%B3%B5-%EC%8B%A0%EA%B2%BD%EB%A7%9D</link>
            <guid>https://velog.io/@h_rin/%ED%98%BC%EC%9E%90-%EA%B3%B5%EB%B6%80%ED%95%98%EB%8A%94-%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-%EB%94%A5%EB%9F%AC%EB%8B%9DChapter-08.-%EC%9D%B4%EB%AF%B8%EC%A7%80%EB%A5%BC-%EC%9C%84%ED%95%9C-%EC%9D%B8%EA%B3%B5-%EC%8B%A0%EA%B2%BD%EB%A7%9D</guid>
            <pubDate>Sat, 07 Dec 2024 11:32:39 GMT</pubDate>
            <description><![CDATA[<h1 id="08-1-합성곱-신경망의-구성-요소">08-1 합성곱 신경망의 구성 요소</h1>
<h2 id="키워드">[키워드]</h2>
<h3 id="합성곱">합성곱</h3>
<p>: 밀집층과 비슷하게 입력과 가중치를 곱하고 절편을 더하는 선형 계산</p>
<ul>
<li>밀집층과 달리 각 합성곱은 입력 전체가 아니라 일부만 사용하여 선형 계산을 수행</li>
</ul>
<h3 id="합성곱-층의-필터">합성곱 층의 필터</h3>
<p>: 밀집층의 뉴런에 해당, 필터의 가중치와 절편을 커널이라고도 함</p>
<ul>
<li>자주 사용되는 크기 (3, 3) 또는 (5, 5)</li>
<li>커널의 깊이는 입력의 깊이와 같음</li>
</ul>
<h3 id="특성맵">특성맵</h3>
<p>: 합성공 층이나 풀링 층의 출력 배열을 의미</p>
<ul>
<li>필터 하나가 하나의 특성 맵을 만듦</li>
<li>합성곱 층에서 5개의 필터를 적용하면 5개의 측성 맵 만들어짐</li>
</ul>
<h3 id="패딩">패딩</h3>
<p>: 합성곱 층의 입력 주위에 추가한 0으로 채워진 픽셀</p>
<ul>
<li>패딩 사용 X -&gt; 밸리드 패딩이라 함</li>
<li>합성곱 층의 출력 크기를 입력과 동일하게 만들기 위해 입력에 패딩을 추가
  -&gt; 세임 패딩</li>
</ul>
<h3 id="스트라이드">스트라이드</h3>
<p>: 합성곱 층에서 필터가 입력 위를 이동하는 크기</p>
<ul>
<li>일반적으로 스트라이드는 1픽셀 사용</li>
</ul>
<h3 id="풀링">풀링</h3>
<p>: 가중치가 없고 특성 맵의 가로세로 크기를 줄이는 역할을 수행</p>
<ul>
<li>대표적으로 최대 풀링과 평균 풀링이 존재, (2, 2) 풀링으로 입력을 절반으로 줄임</li>
</ul>
<h2 id="합성곱-1">합성곱</h2>
<p><img src="https://velog.velcdn.com/images/h_rin/post/aa55b3ea-9e59-4c32-8d38-3ea918b8cc78/image.png" alt="">
[dense의 뉴런 출력 과정]</p>
<h3 id="완전-연결-신경망-밀집층dense의-뉴런">완전 연결 신경망 밀집층(dense)의 뉴런</h3>
<ul>
<li><p>인공 신경망에서는 가중치와 절편을 랜덤하게 초기화한 다음 에포크를 반복하면서 경사 하강법 알고리즘을 사용하여 손실이 낮아지도록 최적의 가중치와 절편을 찾아감</p>
<p>  -&gt; 이것을 &#39;모델 훈련&#39;이라 함
ex) 밀집층에 뉴런이 3개 있다면 출력은 입력 개수와 상관없이 뉴런의 개수를 따라 3개이다. </p>
</li>
</ul>
<h3 id="합성곱-층의-뉴런">합성곱 층의 뉴런</h3>
<ul>
<li>임의로 정한 가중치의 개수 만큼 특성과 곱해져 한 개의 출력을 만들고, 한 칸 이동하여 새로운 출력을 만듦</li>
<li>중요한 것은 첫번째 합성곱에 사용된 가중치와 절편이 두번째부터 끝까지 모든 합성곱에도 동일하게 적용되어야 한다는 점</li>
</ul>
<p><img src="https://velog.velcdn.com/images/h_rin/post/dff4d228-da9c-421e-83d0-ba4dcfcefcc3/image.png" alt="">
<a href="https://velog.io/@nembizzang/Chapter-8.-%EC%9D%B4%EB%AF%B8%EC%A7%80%EB%A5%BC-%EC%9C%84%ED%95%9C-%EC%9D%B8%EA%B3%B5-%EC%8B%A0%EA%B2%BD%EB%A7%9D">convolutional의 뉴런 출력 과정</a></p>
<p><img src="https://velog.velcdn.com/images/h_rin/post/79891241-8ab3-4939-8eab-13cbf55355a6/image.png" alt="">
[convolutional의 뉴런 출력 결과]</p>
<ul>
<li>합성곱 신경망(convolutional neural network, CNN)에서는 완전 연결 신경망과 달리 뉴런을 filter 혹은 kernel이라고 부름</li>
<li>2차원에서도 합성곱층 적용이 가능, 입력이 2차원 배열이면 필터도 2차원이여야 함</li>
</ul>
<p><img src="https://velog.velcdn.com/images/h_rin/post/e0bffb77-1e88-4015-8cbb-9de47a2e6aea/image.png" alt="">
[2차원 배열 합성곱 출력 과정]</p>
<ul>
<li>필터가 이동 가능한 횟수만큼 출력을 만들어낼 수 있고, 이러한 합성곱 계산을 통해 얻은 출력을 특성 맵(feature map)이라 함</li>
</ul>
<p><img src="https://velog.velcdn.com/images/h_rin/post/6dbe47b0-94b3-4a26-a5d8-2c052edfaf9b/image.png" alt="">
[2차원 배열 합성곱 특성 맵 출력]</p>
<ul>
<li>합성곱 층에 있는 필터의 가중치(커널)을 다르게 하여 여러개의 특성 맵(출력)을 얻을 수 있음</li>
</ul>
<p><img src="https://velog.velcdn.com/images/h_rin/post/74409a91-e7eb-4d74-a9a6-8eac87443c0d/image.png" alt="">
[여러개의 특성 맵으로 만들어지는 출력층]</p>
<h2 id="케라스-합성곱-층">케라스 합성곱 층</h2>
<ul>
<li>Conv2D 클래스로 제공됨(왼쪽에서 오른쪽, 위에서 아래로 이동)</li>
<li>매개변수
  1) 필터의 개수(출력의 개수)
  2) 필터에 사용할 커널 크기(kernel_size = ), 보통 (3, 3) 이나 (5, 5) 사용
  3) 활성화 함수(activation = )</li>
</ul>
<pre><code>from tensorflow import keras
keras.layers.Conv2D(10, kernel_size=(3,3), activation=&#39;relu&#39;)</code></pre><h3 id="패딩padding">패딩(padding)</h3>
<ul>
<li><p>(4,4) 크기의 입력에 (3,3) 크기의 커널을 활용하여 출력의 크기를 (2,2)가 아닌 (4,4)로 만드려면?
  -&gt; 실제 입력 크기는 (4,4)이지만 입력 배열의 주위를 가상의 원소를 만들고 0으로 채워서 (6,6)인 입력으로 만든다.</p>
<p>-&gt; 이 과정을 패딩(padding)이라고 함</p>
<p><img src="https://velog.velcdn.com/images/h_rin/post/bf65b382-868c-414b-a22b-fbc57320c8b3/image.png" alt="">
[패딩을 활용한 특성 맵 출력]</p>
</li>
<li><p>패딩의 종류</p>
<blockquote>
<p>세임 패딩(same padding): 
입력과 특성 맵의 크기를 동일하게 만들기 위해 입력 주위에 0 으로 패딩하는것.
  합성곱 신경망에서는 이 방법이 자주 사용됨.</p>
</blockquote>
<blockquote>
<p>밸리드 패딩(valid padding) :
  패딩없이 순수 입력 배열에서만 합성곱을 하여 특성 맵을 만드는 방법으로 특성 맵의 크기가 줄어든다.</p>
</blockquote>
</li>
<li><p>패딩을 사용하는 이유?
: 만약 패딩이 없다면 입력의 모서리 값은 필터를 통과하는 경우가 중앙 값에 비해 현저히 적기때문에 패딩을 통해 모서리에 있는 정보도 특성 맵으로 잘 전달될 수 있도록 해준다. </p>
</li>
</ul>
<p>-&gt; 적절한 패딩은 이미지의 가장자리에 있는 정보를 잃어버리지 않도록 도와준다.</p>
<p><img src="https://velog.velcdn.com/images/h_rin/post/a604a320-71c5-4b53-a6e3-965567bedb1a/image.png" alt="">
[패딩을 사용하여 모서리 정보의 필터 통과 횟수를 증가시킴]</p>
<p>[케라스를 통한 패딩 사용]</p>
<pre><code>keras.layers.Conv2D(10, kernel_size=(3,3), activation=&#39;relu&#39;, padding=&#39;same&#39;)</code></pre><h3 id="스트라이드stride">스트라이드(stride)</h3>
<ul>
<li>커널의 이동을 한 칸에서 두 칸으로 늘리면 그만큼 특성맵의 크기도 작아짐
  -&gt; 스트라이드(stride)라 함</li>
</ul>
<pre><code># stride의 기본값은 1이다.
keras.layers.Conv2D(10, kernel_size=(3,3), activation=&#39;relu&#39;, padding=&#39;same&#39;, strides=1)</code></pre><h3 id="풀링pooling">풀링(pooling)</h3>
<ul>
<li>합성곱 층에서 만든 특성 맵의 가로세로 크기를 줄이는 역할을 함
  -&gt; 특성맵의 개수는 줄이지 않음</li>
</ul>
<p><img src="https://velog.velcdn.com/images/h_rin/post/a6becb91-1d6b-4de9-b23b-e6fa5eb1946a/image.png" alt="">
[풀링을 포함한 합성곱 층 생성 과정]</p>
<ul>
<li><p>풀링의 종류
  : 풀링 또한 합성곱처럼  입력 위를 지나가면서 필터링함
  -&gt; 풀링에는 가중치X, 필터링 영역에서 특정 값을 고름</p>
<blockquote>
<p>최대 풀링(max pooling) : 가장 큰 값을 선택
  <code>keras.layers.MaxPooling2D()</code> 사용 </p>
</blockquote>
<blockquote>
<p>평균 풀링(average pooling) : 평균 값을 선택
  <code>keras.layers.AveragePooing2D()</code> 사용
  -&gt; 평균을 통해 특성 맵의 중요 정보를 희석시킬 수 있기 때문에 잘 사용하지 않음</p>
</blockquote>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/h_rin/post/664c18d8-577a-4e9c-a90c-643ad8c6a7e4/image.png" alt="">
[(2, 2) 최대 풀링 적용]</p>
<ul>
<li><p>풀링 영역은 합성곱 층 커널 이동과는 다르게 겹치지 않고 이동한다.
  -&gt; 스트라이드가 2
  ex) (3, 3) 풀링이면 가로세로 세 칸씩 이동</p>
</li>
<li><p>매개변수
1) 풀링의 크기 : 대부분 2로 설정하여 가로세로 크기를 절반으로 줄인다.
가로세로 방향의 풀링 크기를 정수 튜플로 지정할 수 있으나, 극히 드물다.
2) stride : 기본값이 자동으로 풀링의 크기로 설정되므로 따로 지정할 필요는 없다.
3) padding : 기본값은 valid로 패딩을 하지 않는다. 이 매개변수를 바꾸는 경우는 거의 없다.</p>
</li>
</ul>
<h3 id="합성곱-신경망-구조">합성곱 신경망 구조</h3>
<p>&lt; 입력 크기 = (4,4) / 필터 개수 = 3 / 합성곱층 커널 = (3,3) / padding = same 의 합성곱 신경망 구조&gt;
<img src="https://velog.velcdn.com/images/h_rin/post/5a052a64-4726-4150-a3b0-4be57a625cf4/image.png" alt=""></p>
<ul>
<li><p>합성곱의 필터가 (3,3) 3개 이므로 각각 (3,3) 크기의 가중치를 가지고 있으며 필터마다 절편이 하나씩 있다.</p>
</li>
<li><p>추가적인 설정이 없으면 패딩은 텐서플로에서 자동으로 추가하므로 세임 패딩,
스트라이드는 1이기에 특성 맵의 크기는 입력과 동일한 (4,4)이다.</p>
</li>
<li><p>모든 필터로 만들어진 특성 맵을 합쳐 (4,4,3) 특성 맵이 만들어 진다.</p>
</li>
<li><p>최대 풀링을 사용하여 특성 맵의 크기를 절반으로 줄여 특성맵은 (2,2,3)이 된다.</p>
</li>
<li><p>마지막으로 Flatten 클래스를 이용하여 (2,2,3)을 (12)의 1차원 배열로 변경하여 출력층(dense)의 입력으로 나타낸다.</p>
</li>
</ul>
<p>&lt;풀링 사용하는 이유&gt;
: 합성곱에서 스트라이드를 크게하여 특성 맵을 줄이는 것보다 풀링층에서 줄이는 것이 더 나은 성능을 내기 때문</p>
<h3 id="컬러-이미지를-사용한-합성곱">컬러 이미지를 사용한 합성곱</h3>
<ul>
<li><p>패션 MNIST 데이터는 흑백 이미지이기에 2차원 배열로 표현할 수 있음
  -&gt; 컬러이미지는 RGB로 구성되어 있기에 3차원 배열로 표현
  ex) 입력이 (4, 4, 3)으로 전달됨
<img src="https://velog.velcdn.com/images/h_rin/post/11b5d538-0211-4c2d-969e-b89a9468b426/image.png" alt=""></p>
</li>
<li><p>깊이가 있는 3차원 필터를 사용하여 합성곱을 진행
  : 이때 커널의 크기는 (3,3,3)이 되며 이 합성곱의 계산은 27개의 원소에 27개의 가중치를 곱하고 절편을 더하는 방식</p>
</li>
<li><p>입력이나 필터의 차원이 몇개인지 상관없이 2차원 합성곱과 같이 항상 출력은 하나의 값이고, 이 값이 특성 맵에 있는 하나의 원소가 되는 것</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/h_rin/post/b8b87a1f-286f-49e8-b80f-0ee51a71782a/image.png" alt=""></p>
<ul>
<li>keras 합성곱 층은 할상 3차원 입력을 원함
  -&gt; 따라서 패션 MNIST 데이터처럼 흑백이미지의 경우 깊이 차원에 1을 추가한 3차원 배열로 변환하여 전달</li>
</ul>
<p><img src="https://velog.velcdn.com/images/h_rin/post/420105c3-5252-4ac0-8d77-e4225153c5e8/image.png" alt=""></p>
<ul>
<li><p>합성곱층 - 풀링층 다음에 다시 또 합성곱 층이 오는 경우
<img src="https://velog.velcdn.com/images/h_rin/post/22b22208-a4c3-41d1-9234-ca9ca96fdcad/image.png" alt=""></p>
</li>
<li><p>첫번째 합성곱 층-풀링 층을 통과한 특성 맵의 크기가 (4,4,5)라면,
두번째 합성곱 층의 필터의 너비와 높이가 각각 3일때, 필터의 커널은 (3,3,5)가 된다. 따라서 하나의 필터는 335 = 45개의 가중치를 곱하고 절편을 더한 1개의 출력을 만든다.</p>
</li>
<li><p>이 과정을 통해 두번째 합성곱 층의 필터 개수가 10개라면 특성맵의 크기는 (2,2,10)이 된다. 그리고 마지막에 특성 맵을 모두 flatten하여 dense층의 입력으로 사용한다.</p>
</li>
</ul>
<p>*합성곱 신경망은 너비와 높이는 점점 줄어들고 깊이는 점점 늘어나는 것이 특징</p>
<h1 id="08-2-합성곱-신경망을-사용한-이미지-분류">08-2 합성곱 신경망을 사용한 이미지 분류</h1>
<h2 id="패션-mnist-분류-모델-생성">패션 MNIST 분류 모델 생성</h2>
<h3 id="패션-데이터-불러오기">패션 데이터 불러오기</h3>
<ul>
<li>데이터 불러와서 훈련, 검증, 테스트 셋 나누고, 정규화함<pre><code>from tensorflow import keras
from sklearn.model_selection import train_test_split
</code></pre></li>
</ul>
<h1 id="fashion-mnist-data-set-불러오기">fashion mnist data set 불러오기</h1>
<p>(train_input, train_target), (test_input,test_target) = keras.datasets.fashion_mnist.load_data()</p>
<h1 id="정규화">정규화</h1>
<p>train_scaled = train_input.reshape(-1,28,28,1) / 255.0   # reshape(-1,28,28) : (48000, 28, 28)을 (-1(개수 알아서 맞춰라), 28(행), 28(열), 1(두께))로 바꾸어라</p>
<h1 id="훈련-셋에서-검증-셋-나누기">훈련 셋에서 검증 셋 나누기</h1>
<p>train_scaled, val_scaled, train_target, val_target = train_test_split(train_scaled, train_target, test_size = 0.2, random_state=42)</p>
<pre><code>
- 결과
    하나의 이미지에 대한 깊이(채널) 차원을 넣어주어 (48000,28,28) 크기인 train_input(3차원 배열)이 (48000,28,28,1) 크기인 train_scaled(4차원 배열) 되었다.

### 합성곱 신경망 만들기</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[혼자 공부하는 머신러닝 딥러닝]Chapter 07. 딥러닝 시작]]></title>
            <link>https://velog.io/@h_rin/%ED%98%BC%EC%9E%90-%EA%B3%B5%EB%B6%80%ED%95%98%EB%8A%94-%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-%EB%94%A5%EB%9F%AC%EB%8B%9DChapter-07.-%EB%94%A5%EB%9F%AC%EB%8B%9D-%EC%8B%9C%EC%9E%91</link>
            <guid>https://velog.io/@h_rin/%ED%98%BC%EC%9E%90-%EA%B3%B5%EB%B6%80%ED%95%98%EB%8A%94-%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-%EB%94%A5%EB%9F%AC%EB%8B%9DChapter-07.-%EB%94%A5%EB%9F%AC%EB%8B%9D-%EC%8B%9C%EC%9E%91</guid>
            <pubDate>Sat, 23 Nov 2024 05:42:53 GMT</pubDate>
            <description><![CDATA[<h1 id="07-1-인공-신경망">07-1 인공 신경망</h1>
<h2 id="키워드">[키워드]</h2>
<h3 id="인공-신경망">인공 신경망</h3>
<p>: 생물학적 뉴런에서 영감을 받아 만든 머신러닝 알고리즘</p>
<ul>
<li>이름이 신경망이지만 실제 우리 뇌를 모델링한 것은 아님</li>
<li>종종 딥러닝이라고 부름</li>
</ul>
<h3 id="텐서플로">텐서플로</h3>
<p>: 구글이 만든 딥러닝 라이브러리로 매우 인기가 높음</p>
<ul>
<li>CPU와 GPU를 사용해 인공 신경망 모델을 효율적으로 훈련하며 모델 구축과 서비스에 필요한 다양한 도구 제공</li>
<li>텐서플로 2.0부터 신경망 모델 빠르게 구축할 수 있는 케라스(keras)가 핵심 API</li>
<li>케라스 이용시 간단한 모델에서 복잡한 모델까지 손쉽게 만들 수 o</li>
</ul>
<h3 id="밀집층">밀집층</h3>
<p>: 가장 간단한 인공 신경망의 층</p>
<ul>
<li>인공 신경망에는 여러 종류의 층 존재</li>
<li>밀집층에서 뉴런들이 모두 연결되어 있기 때문에 완전 연결층이라고도 함
❗출력층에 밀집층을 이용시에는 분류하려는 클래스와 동일한 개수의 뉴런 사용</li>
</ul>
<h3 id="원-핫-인코딩">원-핫 인코딩</h3>
<p>: 정수값을 배열에서 해당 정수 위치의 원소만 1이고 나머지는 모두 0으로 변환</p>
<ul>
<li>이 변환의 이유는 다중 분류에서 출력층에서 만든 확률과 크로스 엔트로피 손실을 게산하기 위함</li>
<li>텐서플로에서는 &#39;sparse_categorical_entropy&#39;손실을 지정하면 변환 수행이 필요x</li>
</ul>
<h2 id="패션-mnist">패션 MNIST</h2>
<ul>
<li>&#39;MNIST&#39; 데이터는 원래 손으로 쓴 0~9의 이미지인데, 이건 &#39;패션 MNIST&#39;라서
10종류의 패션 아이템(이미지)으로 구성</li>
</ul>
<h3 id="데이터-가져오기">데이터 가져오기</h3>
<ul>
<li>케라스(keras)의 <code>.load_data()</code>함수는 훈련/테스트 세트 나눠서 데이터 불러옴<pre><code>from tensorflow import keras
</code></pre></li>
</ul>
<p>(train_input, train_target), (test_input, test_target) = keras.datasets.fashion_mnist.load_data()</p>
<pre><code>
- 크기 확인
훈련세트 : 입력은 28x28픽셀 이미지 60,000개 / 타깃은 0~9의 숫자 60,000개
테스트세트 : 훈련세트와 동일한 조건의 이미지 10,000개 / 상응하는 타깃도 마찬가지</code></pre><p>print(train_input.shape, train_target.shape)
print(test_input.shape, test_target.shape)</p>
<p>-&gt;
(60000, 28, 28) (60000,)
(10000, 28, 28) (10000,)</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/d9dd8806-1c6a-475e-b829-53f870b2bfda/image.png)


### 데이터 그려보기
- 패션 MNIST 출력</code></pre><p>import matplotlib.pyplot as plt</p>
<p>fig, axs = plt.subplots(1, 10, figsize=(10,10))
for i in range(10):
    axs[i].imshow(train_input[i], cmap=&#39;gray_r&#39;)
    axs[i].axis(&#39;off&#39;)
plt.show()</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/6e441734-41df-4ddd-87c2-ad1e3447ee96/image.png)

- 샘플 타깃값 각각 확인</code></pre><p>print([train_target[i] for i in range(10)])</p>
<p>-&gt;
[9, 0, 0, 3, 0, 2, 7, 2, 5, 5]</p>
<pre><code>*참고, 패션MNIST에 포함된 10개의 레이블
![](https://velog.velcdn.com/images/h_rin/post/8bfb2550-422c-4ee8-8a42-996c108beeb8/image.png)

- `np.unique()`로 레이블마다 몇 개씩 들어있는지 확인</code></pre><h1 id="0-9-정수로-이루어짐">0-9 정수로 이루어짐</h1>
<p>import numpy as np</p>
<p>print(np.unique(train_target, return_counts=True))</p>
<p>-&gt;
(array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=uint8), array([6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000]))</p>
<pre><code>
## 로지스틱 회귀로 패션 아이템 분류하기
### 모델 선택 &amp; 데이터 전처리
- 훈련 샘플 60,000개, 한 번에 다 사용하는 것보다 하나씩 꺼내서 훈련이 더 효율적
    → 확률적 경사 하강법, 손실함수를 로지스틱으로 한 SGD 사용
- `SGDClassifier` → 표준화 전처리 필요
- 0 ~ 255사이 정수여서, 255로 나눠서 정규화 자주함(이미지 전처리 시 널리 쓰는 일종의 관례)</code></pre><h1 id="표준화-전처리-차원에서-0-255-사이의-픽셀값을-모두-255로-나눔">표준화 전처리 차원에서 0~ 255 사이의 픽셀값을 모두 255로 나눔</h1>
<p>train_scaled = train_input / 255.0 # 정석은 아님, 간편해서 널리 사용
train_scaled = train_scaled.reshape(-1, 28*28) # SGD 쓰기 위해 각 샘플 1차원 배열로 변환</p>
<p>print(train_scaled.shape) # (28, 28) -&gt; 784</p>
<p>-&gt;
(60000, 784)</p>
<pre><code>
### 모델 훈련
- 경사하강법을 활용한 로지스틱 회귀모델을, `cross_validate`로 훈련 &amp; 평가</code></pre><p>from sklearn.model_selection import cross_validate
from sklearn.linear_model import SGDClassifier</p>
<p>sc = SGDClassifier(loss=&#39;log_loss&#39;, max_iter=5, random_state=42)</p>
<p>scores = cross_validate(sc, train_scaled, train_target, n_jobs=-1)
print(np.mean(scores[&#39;test_score&#39;]))</p>
<p>-&gt;
0.8196000000000001</p>
<pre><code>



# 07-2 심층 신경망
## [키워드]
### 심층 신경망
: 2개 이상의 층을 포함한 신경망

### 렐루 함수
: 이미지 분류 모델의 은닉층에 많이 사용하는 활성화 함수
- 시그모이드 함수는 층이 많을수록 활성화 함수의 양쪽 끝에서 변화가 작기 때문에 학습니 어려워지지만 렐루는 이런 문제가 없고 계산이 간단함

### 옵티마이저(Optimizer)
: 신경망의 가중치와 절편을 학습하기 위한 알고리즘 또는 방법을 의미
- 케라스에는 다양한 경사 하강법 알고리즘이 구현되어 있음
    - 대표적으로 SGD, 네스테로프 모멘텀, RMSprop, Adam 등이 존재

## 2개의 층</code></pre><p>from tensorflow import keras</p>
<p>(train_input, train_target), (test_input, test_target) = keras.datasets.fashion_mnist.load_data()</p>
<h1 id="load_data-로-데이터-준비-알아서-훈련테스트-세트-나눠서-반환">.load_data() 로 데이터 준비, 알아서 훈련/테스트 세트 나눠서 반환</h1>
<p>from sklearn.model_selection import train_test_split</p>
<p>train_scaled = train_input / 255.0 # 전처리
train_scaled = train_scaled.reshape(-1, 28*28) # SGD쓰기 위해 1차원으로 표현</p>
<p>train_scaled, val_scaled, train_target, val_target = train_test_split(
    train_scaled, train_target, test_size=0.2, random_state=42)
    # 검증세트 수동으로 덜어내기, 딥러닝은 교차 검증 잘 안함</p>
<pre><code>
### 은닉층(Hidden layer)
: 입력층과 출력층 사이에 있는 모든 층(=밀집층)

- 다중분류이기 때문에 소프트맥스 함수 사용
- 은닉층은 출력층 뉴런 개수보다 많이 두는 것이 일반적임
- 출력층과 마찬가지로 활성화 함수가 적용됨
- 출력층보다는 쓸 수 있는 함수가 비교적 자유로움

![](https://velog.velcdn.com/images/h_rin/post/933ba73e-a907-45a6-93a9-62ffa3314a97/image.png)

*활성화 함수
&gt; 시그모이드 - 이진 분류 모델의 마지막 활성화 함수
소프트맥스 - 다중 분류 모델의 마지막 활성화 함수
ReLU - 기본적으로 은닉층에 사용하는 활성화 함수

+) 회귀를 위한 신경망의 출력층에서는 어떤 활성화 함수를 사용하나?
- 분류문제는 클래스에 대한 확률을 출력하기 위해 활성화 함수를 사용
- 회귀의 출력은 임의의 어떤 숫자이므로 활성화 함수를 적용할 필요 X
    -&gt; 출력층의 선형 방정식의 계산을 그대로 출력

### 심층 신경망 만들기(1)
- 밀집층 객체(Dense)를 만들고 `Seauential()`로 전달해서 만듦
- 은닉층 `activation`(활성함수)은 시그모이드로 설정
</code></pre><h1 id="층-2개로">층 2개로!</h1>
<p>dense1 = keras.layers.Dense(100, activation=&#39;sigmoid&#39;, input_shape=(784,)) # 첫번째 층엔 input_shape 해줌, 유닛 100개인 은닉층 만듦
dense2 = keras.layers.Dense(10, activation=&#39;softmax&#39;) # 유닛 10개인 출력층</p>
<h1 id="만든-층-2개를-쌓아서-심층-신경망-모델-만들기-순서대로-넣으면-됨">만든 층 2개를 쌓아서 &#39;심층 신경망&#39; 모델 만들기, 순서대로 넣으면 됨</h1>
<p>model = keras.Sequential([dense1, dense2])</p>
<h1 id="모델에-대한-정보-확인">모델에 대한 정보 확인</h1>
<p>model.summary()</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/b75b2f30-2729-4bd9-a648-e9d44a2d1278/image.png)

- `.summary()`: 모델에 대한 정보 확인 가능
     - &#39;Output Shape&#39; : (경사하강법 배치크기, 출력 유닛 개수)를 보여줌. 훈련을 몇 개의 배치로 할지 모르니까, 유연하게 받을 수 있도록 None으로 되어있음.
     - &#39;Param&#39; : 모델 파라미터, 즉 각 층의 가중치&amp;절편의 개수를 보여줌.
        1st 층 = 784 * 100 (가중치) + 100(절편) = 78,500
        2nd 층 = 100 * 10 (가중치) + 10(절편) = 1,010

### 심층 신경망 만들기(2)
- 층을 여러 개 추가하는 다른 방법 존재
- 따로 층 객체 만들어서 전달 x -&gt; `Dense()`클래스를 `Sequential()`안에서 만듦
</code></pre><h1 id="name으로-층-이름-설정-가능영문만">name으로 층 이름 설정 가능(영문만)</h1>
<p>model = keras.Sequential([
    keras.layers.Dense(100, activation=&#39;sigmoid&#39;, input_shape=(784,), name=&#39;hidden&#39;),
    keras.layers.Dense(10, activation=&#39;softmax&#39;, name=&#39;output&#39;)
], name=&#39;패션 MNIST 모델&#39;)</p>
<p>model.summary()</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/ed2f38bd-785e-4b7c-805d-4eb5dc75fff0/image.png)

-&gt; 층이 많아지면 한 문장이 너무 길어진다는 단점 존재

### 심층 신경망 만들기(3)
- 모델에 `.add()`로 원하는 만큼 층 추가하는 방법
- `add()`속에서 if문 등을 활용해서 조건에 따라 층을 추가 가능
- 실제로 가장 많이 쓰이는 방법
</code></pre><p>model = keras.Sequential()
model.add(keras.layers.Dense(100, activation=&#39;sigmoid&#39;, input_shape=(784,)))
model.add(keras.layers.Dense(10, activation=&#39;softmax&#39;))</p>
<p>model.summary()</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/0224b4d4-dd5c-4d3e-a63d-269f44f7ef3b/image.png)

- 이후 `.compile()`로 설정하고 `.fit()`으로 훈련되는 과정은 동일</code></pre><p>model.compile(loss=&#39;sparse_categorical_crossentropy&#39;, metrics=[&#39;accuracy&#39;]) #설정해줌</p>
<p>model.fit(train_scaled, train_target, epochs=5) # 훈련, 층 추가하면서 성능 높아짐</p>
<pre><code>
## 활성화 함수
*은닉층에 활성화 함수 적용하는 이유
: 2개의 선형 계산식이 연속된 형태로 있으면 중간 미지수가 사라지듯이, 신경망에서도 연속으로 쌓는 층이 선형적 계산으로만 구성되면 중간에 낀 은닉층들의 존재 의미가 사라짐

![](https://velog.velcdn.com/images/h_rin/post/d3ef8b4d-38c8-4940-9070-e0035df746e6/image.png)

그래서 은닉층의 결과값을 **비선형적으로 만들어주는 계산 필요**
![](https://velog.velcdn.com/images/h_rin/post/b9ab4a33-b91a-4fdc-8627-c6f795285104/image.png)

### 렐루 함수(RELU)
[시그모이드 함수의 단점]
: 출력값(z)이 너무 크거나 작을 땐 함수값의 차이가 너무 작음 
→ 변화에 빠르게 대응하지 못함 → 층을 깊게 쌓기 힘듦

![](https://velog.velcdn.com/images/h_rin/post/9c7a7933-9e96-44ab-8ffb-e3310bdb52ad/image.png)


- 시그모이드 함수의 단점을 보완 -&gt; 렐루 함수
: z&lt;0이면 0으로 출력, z&gt;0이면 그대로 z 출력
(이미지 처리에 좋은 성능을 냄)
![](https://velog.velcdn.com/images/h_rin/post/559c0cab-d27e-49eb-abeb-aaaa059fde23/image.png)

### 심층 신경망 만들기(4) - 렐루함수
- `Flatten()`: 입력데이터를 1차원으로 표현해주는 유틸리티 층(편의를 위한 층)
    → summary 출력해보면 입력값의 개수 바로 알 수 있음
</code></pre><p>model = keras.Sequential()</p>
<h1 id="입력-데이터-1차원으로-펼치는-층">입력 데이터 1차원으로 펼치는 층</h1>
<p>model.add(keras.layers.Flatten(input_shape=(28, 28)))</p>
<h1 id="활성화-함수-렐루로-설정">활성화 함수 렐루로 설정</h1>
<p>model.add(keras.layers.Dense(100, activation=&#39;relu&#39;))
model.add(keras.layers.Dense(10, activation=&#39;softmax&#39;))</p>
<p>model.summary()</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/01639591-db25-4223-9a7c-67aa2acdb8d2/image.png)
→ 모델의 입력값 784개의 1차원 배열이라는 것을 알 수 있음

- 이 모델 훈련 시킴 -&gt; .reshape() 필요없어짐
</code></pre><p>(train_input, train_target), (test_input, test_target) = keras.datasets.fashion_mnist.load_data()</p>
<p>train_scaled = train_input / 255.0</p>
<h1 id="train_scaled--train_scaledreshape1-2828-빠짐-flatten층-생겨서">train_scaled = train_scaled.reshape(1-,28*28) 빠짐, flatten층 생겨서</h1>
<p>train_scaled, val_scaled, train_target, val_target = train_test_split(
    train_scaled, train_target, test_size=0.2, random_state=42)</p>
<p>model.compile(loss=&#39;sparse_categorical_crossentropy&#39;, metrics=[&#39;accuracy&#39;])
model.fit(train_scaled, train_target, epochs=5)</p>
<h1 id="검증세트로-성능-확인---출력층-하나일때-보다-좋아짐">검증세트로 성능 확인 -&gt; 출력층 하나일때 보다 좋아짐</h1>
<p>model.evaluate(val_scaled, val_target)</p>
<pre><code>
## 옵티마이저(Optimizer)
[옵티마이저란?]
&gt; 딥러닝 학습시 최대한 틀리지 않는 방향으로 학습해야 한다,
얼마나 틀리는지(loss)를 알게 하는 함수가 loss function(손실함수)이다.
loss function 의 최솟값을 찾는 것을 학습 목표로 한다.
최소값을 찾아가는 것 최적화 = Optimization
이를 수행하는 알고리즘이 최적화 알고리즘 = Optimizer 이다.

*참고- 옵티마이저 종류
![](https://velog.velcdn.com/images/h_rin/post/04703327-f7c5-4cc2-b3b0-bd184000f8bd/image.png)


### 신경망의 하이퍼파라미터
- 신경망에 특히 하이퍼파라미터가 많음(지정하는 종류로)
ex) 은닉층 뉴런 개수 100, 활성화함수 activation, 층의 종류 Dense Flatten, 배치크기 batch_size, 반복횟수 epochs 등이 있음

### 옵티마이저 설정
- 옵티마이저도 하이퍼파라미터 중 하나, 경사하강법의 종류에 대한 설정이라 보면 됨
- 설정(compile) 단계에서 `optimizer`로 지정 가능, 문자열이나 객체로 만들어 놓고 호출 가능
</code></pre><p>model.compile(optimizer=&#39;sgd&#39;, loss=&#39;sparse_categorical_crossentropy&#39;, metrics=[&#39;accuracy&#39;]) # 기본적인 확률적 경사하강법</p>
<p>sgd = keras.optimizers.SGD()</p>
<h1 id="위-처럼-따로-옵티마이저-객체-만들기학습률-등-세부설정-가능해짐">위 처럼 따로 옵티마이저 객체 만들기(학습률 등 세부설정 가능해짐)</h1>
<p>model.compile(optimizer=sgd, loss=&#39;sparse_categorical_crossentropy&#39;, metrics=[&#39;accuracy&#39;])</p>
<pre><code>- 적응적 학습률: 최적점에 가까워질 수록 학습률을 낮추는 효율적인 방식

![](https://velog.velcdn.com/images/h_rin/post/076f556c-d0dd-4e22-af63-b9127806a487/image.png)

→ 경사를 처음엔 많이 내려가다가 가까워질수록 점점 조금씩 내려가는게 더 효율적임, 이렇게 점차 학습률이 변화하는 것이 &#39;적응적 학습률&#39;
</code></pre><p>sgd = keras.optimizers.SGD(momentum=0.9, nesterov=True) # &#39;네스테로프 모멘텀&#39; 하강법</p>
<pre><code></code></pre><p>adagrad = keras.optimizers.Adagrad() #&#39;아다그라드&#39; 하강법(적응적 학습률)
model.compile(optimizer=adagrad, loss=&#39;sparse_categorical_crossentropy&#39;, metrics=[&#39;accuracy&#39;])</p>
<pre><code></code></pre><p>rmsprop = keras.optimizers.RMSprop() # &#39;RMSprop&#39; 하강법 (적응적 학습률)
model.compile(optimizer=rmsprop, loss=&#39;sparse_categorical_crossentropy&#39;, metrics=[&#39;accuracy&#39;])</p>
<pre><code>
### 심층 신경망 만들기(5)
- Adam 옵티마이저를 사용한 모델로 만들어 봄
(Sequential &amp; add → compile → fit → evaluate 순서)
</code></pre><p>model = keras.Sequential()
model.add(keras.layers.Flatten(input_shape=(28, 28)))
model.add(keras.layers.Dense(100, activation=&#39;relu&#39;))
model.add(keras.layers.Dense(10, activation=&#39;softmax&#39;))</p>
<p>model.compile(optimizer=&#39;adam&#39;, loss=&#39;sparse_categorical_crossentropy&#39;, metrics=[&#39;accuracy&#39;])</p>
<p>model.fit(train_scaled, train_target, epochs=5)</p>
<pre><code>
*참고 - 앙상블 알고리즘 🆚 심층 신경망
- 여러 개를 모아서 하나의 최종 모델을 구성한다는 점에서 비슷해보이지만,
완전히 다른 방식으로 훈련

![](https://velog.velcdn.com/images/h_rin/post/49ebe6a0-21df-4717-b7e2-4ca9f9c7b54b/image.png)

[그림출처](https://velog.io/@simon919/7-2.-%EC%8B%AC%EC%B8%B5-%EC%8B%A0%EA%B2%BD%EB%A7%9D)


# 07-3 신경망 모델 훈련
## [키워드]
### 드롭아웃
: 은닉층에 있는 뉴런의 출력을 랜덤하게 꺼서 과대적합을 막는 기법
- 훈련 중에 적용, 평가나 예측에서 적용하지 X (텐서플로는 자동으로 처리)

### 콜백
: 케라스 모델을 훈련하는 도중에 어떤 작업을 수행할 수 있도록 도와주는 도구
- 대표적으로 최상의 모델을 자동으로 저장해 주거나 검증 점수가 더 이상 향상되지 않으면 일찍 종료 가능

### 조기 종료
: 검증 점수가 더 이상 감소하지 않고 상승하여 과대적합이 일어나면 훈련을 계속 진행하지 않고 멈추는 기법
- 계산 비용과 시간 절약 가능

## 손실곡선
### 모델 만드는 함수 제작</code></pre><p>from tensorflow import keras
from sklearn.model_selection import train_test_split</p>
<p>(train_input, train_target), (test_input, test_target) = <br>    keras.datasets.fashion_mnist.load_data()</p>
<p>train_scaled = train_input / 255.0</p>
<p>train_scaled, val_scaled, train_target, val_target = train_test_split(
    train_scaled, train_target, test_size=0.2, random_state=42)</p>
<h1 id="모델-함수화">모델 함수화</h1>
<p>def model_fn(a_layer=None):
    model = keras.Sequential()
    model.add(keras.layers.Flatten(input_shape=(28, 28)))
    model.add(keras.layers.Dense(100, activation=&#39;relu&#39;))
    if a_layer: # 은닉층 뒤에 원하는 층 더 추가 가능하게 설정
        model.add(a_layer)
    model.add(keras.layers.Dense(10, activation=&#39;softmax&#39;)) 
    # 마지막은 전과 동일한 출력층
    return model</p>
<p>model = model_fn()</p>
<p>model.summary()</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/ffeb4fed-6ec1-4079-9d0a-1bbc6b94bfd1/image.png)


### fit의 결과값 시각화
- keras의 `.fit()`메소드는 History 객체를 반환함
↪ 그 안에는 훈련측정값이 담긴 history 딕셔너리가 들어있음! (&#39;accuracy&#39;는 우리가 따로 metrics 넣어줬기 때문에 있는 거임 ㅇㅇ)
</code></pre><h1 id="똑같이-fit하고-그-결과를-history에-담아봄">똑같이 fit하고 그 결과를 history에 담아봄</h1>
<p>model.compile(loss=&#39;sparse_categorical_crossentropy&#39;, metrics=[&#39;accuracy&#39;])
history = model.fit(train_scaled, train_target, epochs=5, verbose=0)</p>
<h1 id="결과-담긴-history-객체에서-key-값-꺼내보면--손실--정확도">결과 담긴 history 객체에서 key 값 꺼내보면 =&gt; 손실 &amp; 정확도</h1>
<p>print(history.history.keys())</p>
<pre><code>-&gt; dict_keys([&#39;accuracy&#39;, &#39;loss&#39;])

- 위 결과값들을 그래프로 그려봄
</code></pre><p>import matplotlib.pyplot as plt</p>
<p>plt.plot(history.history[&#39;loss&#39;]) # 인덱스 0부터, loss값까지
plt.xlabel(&#39;epoch&#39;) # 에포크가 0부터 시작하니까 인덱스와 같음
plt.ylabel(&#39;loss&#39;)
plt.show()</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/35703b86-a42b-4d65-afa4-389f2167468e/image.png)

- 에포크가 늘어날수록 loss 감소, accuracy 늘어남</code></pre><p>plt.plot(history.history[&#39;accuracy&#39;])
plt.xlabel(&#39;epoch&#39;)
plt.ylabel(&#39;accuracy&#39;)
plt.show()</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/69969800-d973-43f6-b15c-2ca458eb9db8/image.png)

### 에포크 늘리기
- 에포크를 더 늘릴수록 손실이 더욱 감소하는 것을 확인
- `verbose`: 훈련 과정 출력 조절, 기본값은 1 -&gt; 에포크마다 진행 막대와 함께 손실 등의 지표 출력 / 2로 바꾸면 진행 막대 빼고 출력 / 0이면 훈련과정 안나타냄
</code></pre><p>model = model_fn() #기본 모델
model.compile(loss=&#39;sparse_categorical_crossentropy&#39;, metrics=[&#39;accuracy&#39;])</p>
<p>history = model.fit(train_scaled, train_target, epochs=20, verbose=0)</p>
<h1 id="에포크-20으로-늘려서">에포크 20으로 늘려서</h1>
<p>plt.plot(history.history[&#39;loss&#39;]) 
plt.xlabel(&#39;epoch&#39;)
plt.ylabel(&#39;loss&#39;)
plt.show()</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/679cc594-a7b4-41c1-9003-5aa5f5cf01de/image.png)

### 검증손실과 과대적합
- 검증세트의 손실도 확인해야 함
![](https://velog.velcdn.com/images/h_rin/post/b5f93365-cbbd-430f-8027-f0fda9cbcb5c/image.png)

- `validation_data`: fit의 매개변수, 검증세트의 결과도 반환하도록 설정 가능
</code></pre><p>model = model_fn()
model.compile(loss=&#39;sparse_categorical_crossentropy&#39;, metrics=[&#39;accuracy&#39;])</p>
<p>history = model.fit(train_scaled, train_target, epochs=20, verbose=0,
                    validation_data=(val_scaled, val_target)) </p>
<h1 id="검증세트-결과도-반환해줘">검증세트 결과도 반환해줘</h1>
<p>print(history.history.keys()) # key 하나 더 추가됨</p>
<pre><code>-&gt; dict_keys([&#39;accuracy&#39;, &#39;loss&#39;, &#39;val_accuracy&#39;, &#39;val_loss&#39;])

- 검증세트의 손실 같이 그래프로 그림</code></pre><p>plt.plot(history.history[&#39;loss&#39;])
plt.plot(history.history[&#39;val_loss&#39;])
plt.xlabel(&#39;epoch&#39;)
plt.ylabel(&#39;loss&#39;)
plt.legend([&#39;train&#39;, &#39;val&#39;])
plt.show()</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/a04e6ba2-0a50-4a98-a483-3a47368ecf48/image.png)
-&gt; 훈련세트에만 너무 잘맞는 과대적합이였음을 확인 가능함

## 드롭아웃
- 신경망 모델에만 있는 규제 방법으로 딥러닝의 아버지 Geoffrey Hinton이 소개하심
- 층에 있는 유닛을 다 훈련X, 일부를 랜덤하게 off해서 훈련 성능 낮춤
    ex)  1st 샘플에선 두번째 유닛 계산 안 하고, 2nd 샘플에선 첫번째 유닛 계산 안 하고,•••
![](https://velog.velcdn.com/images/h_rin/post/c4bfe17b-1880-41f4-ba46-65e23e089932/image.png)
[그림 출처](https://velog.io/@simon919/%ED%98%BC%EA%B3%B5%EB%A8%B8%EC%8B%A0-7-3.-%EC%8B%A0%EA%B2%BD%EB%A7%9D-%EB%AA%A8%EB%8D%B8-%ED%9B%88%EB%A0%A8)

- `Dropout()`: 드롭아웃 기능을 제공하는 클래스. 얼마나 drop할지 비율을 정해야 함
(*마치 케라스 층처럼 사용되지만, 학습되는 모델 파라미터는 없음)
</code></pre><h1 id="드롭아웃으로-랜덤하게-유닛들-off하면서-훈련--훈련세트-성능-규제">드롭아웃으로 랜덤하게 유닛들 off하면서 훈련 = 훈련세트 성능 규제</h1>
<p>model = model_fn(keras.layers.Dropout(0.3)) # 층처럼 쌓음</p>
<p>model.summary()</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/c016a99f-9cbb-4803-8ad9-632e3ff00e3b/image.png)

- 드롭아웃 처리한 모델의 검증손실을 출력하니 과대적합 완화됨
(따로 Dropout 층 안 빼줘도 알아서 평가&amp;예측 시에는 드롭아웃 적용 안 함)
</code></pre><h1 id="드롬아웃-처리한-걸로-다시-훈련">드롬아웃 처리한 걸로 다시 훈련</h1>
<p>model.compile(optimizer=&#39;adam&#39;, loss=&#39;sparse_categorical_crossentropy&#39;,
              metrics=[&#39;accuracy&#39;])</p>
<p>history = model.fit(train_scaled, train_target, epochs=20, verbose=0,
                    validation_data=(val_scaled, val_target))</p>
<p>plt.plot(history.history[&#39;loss&#39;])
plt.plot(history.history[&#39;val_loss&#39;])
plt.xlabel(&#39;epoch&#39;)
plt.ylabel(&#39;loss&#39;)
plt.legend([&#39;train&#39;, &#39;val&#39;])
plt.show()</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/0f594658-f579-4113-9add-2b8fee9574cf/image.png)

-&gt; 최적의 에포크 10정도 되어 보임

## 모델 저장 &amp; 복원
- 에포크 10으로 다시 훈련</code></pre><p>model = model_fn(keras.layers.Dropout(0.3))
model.compile(optimizer=&#39;adam&#39;, loss=&#39;sparse_categorical_crossentropy&#39;,
              metrics=[&#39;accuracy&#39;])</p>
<p>history = model.fit(train_scaled, train_target, epochs=10, verbose=0,
                    validation_data=(val_scaled, val_target))</p>
<pre><code>
### 모델 저장
- `.save_weights()`: 훈련된 모델의 가중치를 저장하는 메소드
- `.save()`: 훈련된 모델의 구조와 가중치를 통째로 저장하는 메소드
</code></pre><p>model.save(&#39;model-whole.keras&#39;) # 가중치랑 모델 구조까지 다 저장</p>
<p>model.save_weights(&#39;model.weights.h5&#39;) # 모델의 가중치만 저장</p>
<p>!ls -al model* # 잘 만들어졌나 셀 명령으로 확인</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/fe9ac795-04e0-4546-b933-b7e331b3c0ef/image.png)

### 모델 복원
- `.load_weights()`: 이전에 저장했던 모델의 가중치를 적재하는(불러오는) 메소드
    (모델 구조가 정확히 같아야만 적재할 수 있음)
- `.load_model()`: 이전에 저장했던 모델 전체를 불러오는 메소드
</code></pre><p>model = model_fn(keras.layers.Dropout(0.3)) # 새 모델 만들고</p>
<p>model.load_weights(&#39;model.weights.h5&#39;) # 만든 가중치 반영</p>
<pre><code>
### 검증세트의 정확도 계산
#### 복원_1 모델
&gt;evaluate() 써도 되지만, 그렇게 계산하려면 복원한 모델에 또 다시 compile()을 실행해야 함. 여기에선 그냥 새로운 데이터에 대해 정확도만 계산하면 되는 상황이라고 가정

[계산 방법]
: 예측 클래스 직접 구하고 타겟이랑 비교해서 맞춘 비율로 계산

- `.predict()`: 샘플마다 각 클래스일 확률을 반환해줌(사이킷런 predict_proba처럼)
* 사이킷런에서는 예측 클래스가 뭔지 바로 반환함, 여기서는 모든 클래스별로 확률을 반환하기 때문에 그 중에 제일 높은 걸 다시 선택하는 과정을 수동으로 해줘야 함

- `.argmax()`: predict가 출력한 확률들 중에 가장 큰 값을 뽑기 위해 넘파이 활용
    -&gt; `axis=-1`: 배열의 마지막 차원(여기서는 axis=1)을 따라 argmax를 수행
- 그렇게 뽑은 최댓값의 인덱스 `val_labels`와 `val_target`을 비교해서, 일치하는 비율이 곧 검증세트의 정확도가 됨
</code></pre><p>import numpy as np # 넘파이의 argmax 활용</p>
<p>val_labels = np.argmax(model.predict(val_scaled), axis=-1)</p>
<h1 id="모델이-구한-예측-클래스와-실제-타겟값-비교해서-맞춘-비율-알아봄">모델이 구한 예측 클래스와 실제 타겟값 비교해서 맞춘 비율 알아봄</h1>
<p>print(np.mean(val_labels == val_target))</p>
<pre><code>
#### 복원_2 모델
&gt;  이 경우는 모델의 구조와 옵티마이저까지 모두 그대로 복원했기 때문에 바로 evaluate()를 사용할 수 있음

- `.load_model()`: 이전에 저장했던 모델 전체를 불러오는 메소드
</code></pre><h1 id="모델-전체-저장했던-걸로-해봄">모델 전체 저장했던 걸로 해봄</h1>
<p>model = keras.models.load_model(&#39;model-whole.keras&#39;)</p>
<p>model.evaluate(val_scaled, val_target)</p>
<pre><code>
## 콜백
- 매번 수동으로 다시 최적의 에포크 설정, 모델 훈련하는 번거로움 해결 =&gt; 콜백(callback)
- 훈련 과정 중간에 특정 작업을 수행하게 해주는 객체, `keras.callbacks` 패키지 아래에 다양한 클래스 존재
    -&gt; 콜백 객체 만들어두고, fit()할때 callbacks 매개변수로 전달

### ModelCheckpoint 콜백
- `ModelCheckpoint()`: 가장 자주 사용되는 콜백, 에포크마다 모델을 저장
- `save_best_only=True`: 손실이 가장 낮은 모델만 저장하도록 하는 설정
</code></pre><p>model = model_fn(keras.layers.Dropout(0.3))
model.compile(optimizer=&#39;adam&#39;, loss=&#39;sparse_categorical_crossentropy&#39;,
              metrics=[&#39;accuracy&#39;])</p>
<p>checkpoint_cb = keras.callbacks.ModelCheckpoint(&#39;best-model.keras&#39;,
                                                save_best_only=True)</p>
<p>model.fit(train_scaled, train_target, epochs=20, verbose=0,
          validation_data=(val_scaled, val_target),
          callbacks=[checkpoint_cb])</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/bb04f2f4-efe9-4b1e-aeed-e4b2e7044b56/image.png)

![](https://velog.velcdn.com/images/h_rin/post/37acc106-1560-491f-85fe-2c989dfe9a5d/image.png)


### EarlyStopping 콜백
- 최적의 모델 찾을 때 에포크를 무작정 높게 설정해놓고 찾으면, 불필요하게 오랫동안 훈련을 계속함
- `EarlyStopping()`: 과대적합이 시작되면 훈련을 알아서 조기종료 해주는 콜백
- `patience`: 검증세트 성능이 좋아지지 않더라도 참고 기다릴 에포크 횟수 설정
- `restore_best_weights=True`: 훈련동안 가장 손실 낮았던 최적 가중치로 돌리는 설정
![](https://velog.velcdn.com/images/h_rin/post/de92db2d-0734-4454-99c9-d68788b0928e/image.png)

- fit()에서 epochs를 크게 설정해도 ㄱㅊ
- `.stopped_epoch`: 몇 번째 에포크에서 조기종료 했는지 저장되어 있는 속성
    → patience 설정했던 것과 같이 생각해보면 최상의 에포크가 언제인지 나옴

![](https://velog.velcdn.com/images/h_rin/post/4799e4ad-b0c4-4b4a-ab44-eae96ebafb39/image.png)

### 최종 모델
- ModelCheckpoint 콜백이랑 EarlyStopping 콜백을 함께 사용
    → 자동으로 최상의 모델 얻음
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[혼자 공부하는 머신러닝 딥러닝]Chapter 06. 비지도 학습]]></title>
            <link>https://velog.io/@h_rin/%ED%98%BC%EC%9E%90-%EA%B3%B5%EB%B6%80%ED%95%98%EB%8A%94-%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-%EB%94%A5%EB%9F%AC%EB%8B%9DChapter-06.-%EB%B9%84%EC%A7%80%EB%8F%84-%ED%95%99%EC%8A%B5</link>
            <guid>https://velog.io/@h_rin/%ED%98%BC%EC%9E%90-%EA%B3%B5%EB%B6%80%ED%95%98%EB%8A%94-%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-%EB%94%A5%EB%9F%AC%EB%8B%9DChapter-06.-%EB%B9%84%EC%A7%80%EB%8F%84-%ED%95%99%EC%8A%B5</guid>
            <pubDate>Sat, 16 Nov 2024 06:57:39 GMT</pubDate>
            <description><![CDATA[<h1 id="06-1-군집-알고리즘">06-1 군집 알고리즘</h1>
<h2 id="키워드">[키워드]</h2>
<h3 id="비지도-학습">비지도 학습</h3>
<p>: 머신러닝의 한 종류, 훈련 데이터에 타깃X(= 입력 데이터만 있음) -&gt; 외부의 도움없이 스스로 학습
즉, 알고리즘이 스스로 데이터 속의 패턴을 찾아냄
ex) 군집, 차원 축소 등</p>
<h3 id="히스토그램">히스토그램</h3>
<p>: 구간별로 값이 발생한 빈도를 그래프로 표시한 것</p>
<ul>
<li>보통 x축이 값의 구간(계급)이고 y축이 발생 빈도(도수)</li>
</ul>
<h3 id="군집">군집</h3>
<p>: 비슷한 샘플끼리 하나의 그룹으로 모으는 대표적인 비지도 학습 작업</p>
<ul>
<li>군집 알고리즘으로 모은 샘플 그룹을 클러스터라고 부름</li>
</ul>
<h2 id="사진-데이터-다루기">사진 데이터 다루기</h2>
<h3 id="데이터-가져오기">데이터 가져오기</h3>
<ul>
<li>넘파이 배열로 저장된 파일 다운로드</li>
<li><code>!</code>: 리눅스 shell 명령으로 바꿔줌</li>
<li><code>wget</code>: 웹에서 파일을 다운로드하여 저장하는 명령어(-0 뒤에는 파일 이름)</li>
</ul>
<pre><code># 과일 데이터 준비
!wget https://bit.ly/fruits_300_data -O fruits_300.npy</code></pre><ul>
<li>다운받은 파일 불러오고 배열의 크기 확인 -&gt; 3차원 데이터</li>
<li><code>np.load()</code>: npy파일 불러오는 메소드</li>
</ul>
<pre><code>import numpy as np
import matplotlib.pyplot as plt

# 다운로드 받은 파일 불러오기
fruits = np.load(&#39;fruits_300.npy&#39;)

print(fruits.shape)
# 가로세로 100x100, 300개
-&gt;
(300, 100, 100)</code></pre><ul>
<li>첫번째 행(가로)에 있는 픽셀 100개 값 출력</li>
<li>이 배열은 흑백, 0~255 정수값 가짐(숫자 클수록 밝음)
(<code>.shape</code>결과와 동일한 순서로 인덱싱 = 샘플, 가로, 세로)<pre><code>print(fruits[0, 0, :])
</code></pre></li>
</ul>
<p>-&gt;
[  1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   2   1
   2   2   2   2   2   2   1   1   1   1   1   1   1   1   2   3   2   1
   2   1   1   1   1   2   1   3   2   1   3   1   4   1   2   5   5   5
  19 148 192 117  28   1   1   2   1   4   1   1   3   1   1   1   1   1
   2   2   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]</p>
<pre><code>
### 데이터 그리기
- `plt.imshow()`: 넘파이 배열에 저장된 이미지를 그림
-  `cmap`: 이미지 색상 테마를 지정해줌
</code></pre><p>plt.imshow(fruits[0], cmap=&#39;gray&#39;)
plt.show()</p>
<pre><code>
![](https://velog.velcdn.com/images/h_rin/post/8f124416-f1f2-4c3d-8cf1-5fadccfea641/image.png)

=&gt; 위에서 첫번째 행을 출력했을 때 사진 상에서 맨 위에 꼭지 부분에 색이 달라서 숫자가 크게 나온 것을 알 수 있음

- 색 반전을 위해 `gray_r`을 사용, 원래랑 거꾸로 낮은 숫자가 높아지고 높은 숫자가 낮아짐
</code></pre><p>plt.imshow(fruits[0], cmap=&#39;gray_r&#39;)
plt.show()</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/5e87ccea-a7bf-44a2-af38-6aee4b341c2d/image.png)

- 파인애플과 바나나도 그려보기
- `plt.subplots(a, b)`: a행 b열로 그래프 여러 개를 나란히 그려주는 함수
- `axs`: `subplot`으로 그린 여러 그래프를 갖고 있는 리스트 배열
</code></pre><p>fig, axs = plt.subplots(1, 2) # 1행 2열로 그래프 배치
axs[0].imshow(fruits[100], cmap=&#39;gray_r&#39;) # 100번째 사진
axs[1].imshow(fruits[200], cmap=&#39;gray_r&#39;) # 200번째 사진
plt.show()</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/3b3af43e-fa19-4808-b280-25c9544a0de2/image.png)


## 픽셀값 분석하기
### 1차원 배열로 변환
- 각 이미지 데이터(100x100)를 2차원-&gt;1차원으로 바꿈(복잡하니까)
    -&gt; **길이가 10,000인 1차원 배열**이 됨
![](https://velog.velcdn.com/images/h_rin/post/b59def10-eb9c-4c52-afab-a12e0d6fb283/image.png)

-`.reshape`를 사용해서 (100, 100, 100)이던 apple / pineapple / banana
-&gt; 두번째 차원(100)과 세번째 차원(100)을 10,000(100, 100*100)으로 합침
- 첫번째 차원을 -1로 지정하면 자동으로 남은 차원 할당
</code></pre><p>apple = fruits[0:100].reshape(-1, 100<em>100) # 과일 100개씩 있음
pineapple = fruits[100:200].reshape(-1, 100</em>100) # 다 하나하나 길게 펼쳐줌
banana = fruits[200:300].reshape(-1, 100*100)</p>
<p>print(apple.shape)</p>
<p>-&gt;
(100, 10000) # 100개의 샘플, 샘플 당 10,000픽셀</p>
<pre><code>
### 샘플의 픽셀 평균값
&gt; 넘파이 배열 계산 시 주의
![](https://velog.velcdn.com/images/h_rin/post/3368001a-0549-4f37-9855-ce36ff7a4394/image.png)


- 각 샘플마다 픽셀의 평균값 계산해야 함 -&gt; `mean()`메서드 사용, 평균을 계산할 축 지정해줘야하는데 `axis=0`사용
ex) `axis=0`: 첫 번째 축인 행따라 계산, `axis=1`: 두번째 축인 열따라 계산

![](https://velog.velcdn.com/images/h_rin/post/1a3c2995-6677-45a6-9892-e136cb742a14/image.png)


- 사과 100개에 대한 픽셀 평균값 계산
</code></pre><p>print(apple.mean(axis=1))</p>
<p>-&gt;
[ 88.3346  97.9249  87.3709  98.3703  92.8705  82.6439  94.4244  95.5999
  90.681   81.6226  87.0578  95.0745  93.8416  87.017   97.5078  87.2019
  88.9827 100.9158  92.7823 100.9184 104.9854  88.674   99.5643  97.2495
  94.1179  92.1935  95.1671  93.3322 102.8967  94.6695  90.5285  89.0744
  97.7641  97.2938 100.7564  90.5236 100.2542  85.8452  96.4615  97.1492
  90.711  102.3193  87.1629  89.8751  86.7327  86.3991  95.2865  89.1709
  96.8163  91.6604  96.1065  99.6829  94.9718  87.4812  89.2596  89.5268
  93.799   97.3983  87.151   97.825  103.22    94.4239  83.6657  83.5159
 102.8453  87.0379  91.2742 100.4848  93.8388  90.8568  97.4616  97.5022
  82.446   87.1789  96.9206  90.3135  90.565   97.6538  98.0919  93.6252
  87.3867  84.7073  89.1135  86.7646  88.7301  86.643   96.7323  97.2604
  81.9424  87.1687  97.2066  83.4712  95.9781  91.8096  98.4086 100.7823
 101.556  100.7027  91.6098  88.8976]</p>
<pre><code>
- 세 과일 모두 평균 계산하고 히스토그램으로 그려서 비교
- `alpha`: 그래프 투명도 설정
- `plt.legend`: 범례 첨부 설정
</code></pre><p>plt.hist(np.mean(apple, axis=1), alpha=0.8)
plt.hist(np.mean(pineapple, axis=1), alpha=0.8)
plt.hist(np.mean(banana, axis=1), alpha=0.8)
plt.legend([&#39;apple&#39;, &#39;pineapple&#39;, &#39;banana&#39;])
plt.show()</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/23fd4d2c-4c96-4ef4-b075-b34cd3fecaca/image.png)

=&gt; 바나나는 값들이 40 이하에 집중되어있어 이 값을 활용해 구분 가능
but, 사과랑 파인애플은 어찌해야될까

### 픽셀의 샘플 평균값
- `axis=0`으로 픽셀마다 샘플의 평균값 계산
</code></pre><p>fig, axs = plt.subplots(1, 3, figsize=(20, 5))
axs[0].bar(range(10000), np.mean(apple, axis=0))
axs[1].bar(range(10000), np.mean(pineapple, axis=0))
axs[2].bar(range(10000), np.mean(banana, axis=0))
plt.show()</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/73c5421c-11d6-42b5-9253-743f061f1631/image.png)
=&gt; 과일마다 값이 높은 구간이 다 다름

## 평균 이미지 활용하기
### 평균 이미지와 가까운 사진 고르기
- 샘플 평균값 `np.mean(apple, axis=0)` -&gt; 2차원 배열로 이미지처럼 출력(100x100)크기
</code></pre><p>apple_mean = np.mean(apple, axis=0).reshape(100, 100)
pineapple_mean = np.mean(pineapple, axis=0).reshape(100, 100)
banana_mean = np.mean(banana, axis=0).reshape(100, 100)</p>
<p>fig, axs = plt.subplots(1, 3, figsize=(20, 5))
axs[0].imshow(apple_mean, cmap=&#39;gray_r&#39;)
axs[1].imshow(pineapple_mean, cmap=&#39;gray_r&#39;)
axs[2].imshow(banana_mean, cmap=&#39;gray_r&#39;)
plt.show()</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/586f4a9a-0d4c-4604-9784-336b0dd809e9/image.png)
=&gt; 픽셀마다 샘플들이 차지하는 값을 평균 낸 이미지
- 모든 사진을 합쳐놓은 &#39;대표 이미지&#39;처럼 보임

&gt; 고객이 올린 사진을 &#39;대표 이미지&#39;와 비교하면 어떤 과일인지 구분 할 수 있지 않을까?, 대표 이미지와 차이가 가장 적은 것으로 채택하면 됨


- 그렇다면 우리가 가진 300개의 사진 중 사과의 대표 이미지와 가장 가까운 사진을 고르자

-`apple_mean`: 사과의 대표 이미지(= 사과 사진 100장의 평균 이미지)
- `.abs()`: 절대값으로 계산하는 함수(absolute)
</code></pre><p>abs_diff = np.abs(fruits - apple_mean)
abs_mean = np.mean(abs_diff, axis=(1,2))
print(abs_mean.shape)</p>
<p>-&gt;
(300,)</p>
<pre><code>
- `abs_mean`: 차이값 요약한 값 -&gt; 가장 작은 샘플이 가장 사과에 가까운 것
- `np.arsort()`: 작은 값부터 순서대로 인덱스 반환하는 함수

- 처음 100개에 대해서 작은 순으로 나열해봄
- 인덱스 중 (i*10+j)번째 것을 [i, j]위치에 그림으로 그려봄
    -&gt; 사과 대표 이미지에 가장 가까운 것부터 100개의 그림이 그려짐
</code></pre><p>apple_index = np.argsort(abs_mean)[:100]
fig, axs = plt.subplots(10, 10, figsize=(10,10))
for i in range(10):
    for j in range(10):
        axs[i, j].imshow(fruits[apple_index[i*10 + j]], cmap=&#39;gray_r&#39;)
        axs[i, j].axis(&#39;off&#39;)
plt.show()</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/2e13bd37-447d-4aff-babb-cde100ec8983/image.png)

↪ abs_mean에서 처음 100개만 했으니, 모두 사과로 나오면 OK!

### 군집(clustering)
- 사진들이 갖고 있는 픽셀값을 사용해 과일 사진을 모으는 작업을 해봄
→ 이렇게 비슷한 샘플끼리 그룹으로 모으는 작업을 군집이라고 하며, 군집 알고리즘을 통해 묶은 그룹들을 클러스터(cluster)라고 부름. (대표적인 비지도학습)
- 근데 사실 이번 시간에는, 사과/파인애플/바나나의 평균값을 계산해서 찾은 거니까
사실상 타깃이 있는 셈 (≠비지도 학습)
→ 실제 비지도 학습에서는 이렇게 샘플의 평균값들을 미리 구할 수 없음

[참고/인용](https://velog.io/@simon919/%ED%98%BC%EA%B3%B5%EB%A8%B8%EC%8B%A0-6-1.-%EA%B5%B0%EC%A7%91-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-acw7480n)

# 06-2 k-평균
## [키워드]
### k-평균
: k-평균 알고리즘은 처음에 랜덤하게 클러스터 중심을 정하고 클러스터를 만듦
-&gt; 그 다음 클러스터의 중심을 이동하고 다시 클러스터를 만드는 식으로 반복해서 최적의 클러스터 구성을 만듦

### 클러스터 중심
: k-평균 알고리즘이 만든 클러스터에 속한 샘플의 특성 평균값으로 센트로이드(centroid)라고도 함
- 가장 가까운 클러스터 중심을 샘플의 또 다른 특성으로 사용하거나 새로운 샘풀에 대한 예측으로 활용가능

### 엘보우 방법
: 최적의 클러스터 개수를 정하는 방법 중 하나
- 이너셔: 샘플 사리 거리의 제곱의 합
- 클러스터 개수에 따라 이너셔 감소가 꺾이는 지점이 적절한 클러스터 개수 k가 될 수 있음
- 그래프의 모양을 따서 엘보우라고 함

## k-평균 알고리즘이란?
[작동 방식]
1. 무작위로 k개릐 클러스터 중심을 정함
    - &#39;클러스터 중심&#39;이란 앞선 `apple_mean`같은 평균값(대표 이미지를 구성하는 픽셀 10,000개)를 다르게 표현한 것
2. 각 샘플에서 가장 가까운 클러스터 중심을 찾아 해당 클러스터의 샘플로 지정
    - 가장 가까운 &#39;몇 개&#39;를 할지 -&gt; `n_clusters`로 설정
3. 클러스터에 속한 샘플의 평균값으로 클러스터 중심을 변경
    - 처음과 다른 k개의 평균값이 구해짐(클러스터 중심 이동)
4. 클러스터 중심에 변화가 없을 때까지 2번으로 돌아가 반복

![](https://velog.velcdn.com/images/h_rin/post/fad0abef-72a0-4816-b447-3e3b92415134/image.png)

## KMeans 클래스
### 모델 훈련하기
- 300개의 과일 데이터 준비
</code></pre><p>!wget <a href="https://bit.ly/fruits_300_data">https://bit.ly/fruits_300_data</a> -O fruits_300.npy</p>
<p>import numpy as np</p>
<p>fruits = np.load(&#39;fruits_300.npy&#39;)</p>
<pre><code>
- 샘플개수, 너비, 높이 크기의 3차원 배열을 (샘플개수, 너비*높이)크기의 2차원 배열로 바꿔줌
</code></pre><p>fruits_2d = fruits.reshape(-1, 100*100)</p>
<pre><code>
- `KMeans()`: 사이킷런에서 k-평균 알고리즘이 구현되어 있는 클래스
- `n_clusters`: 클러스터 개수를 지정하는 매개변수
</code></pre><p>from sklearn.cluster import KMeans</p>
<p>km = KMeans(n_clusters=3, random_state=42) # 클러스터 3개
km.fit(fruits_2d) # fit으로 모델 훈련, 타깃x -&gt; 입력데이터만 전달</p>
<h1 id="타깃을-모르는-상태로-모델이-직접-학습하는-것---비지도-학습">타깃을 모르는 상태로 모델이 직접 학습하는 것 -&gt; &#39;비지도 학습&#39;</h1>
<pre><code>
- 군집(clustering)의 결과는 `.lebels_`에 저장되어 있음
</code></pre><p>print(km.labels_)</p>
<p>-&gt;
[2 2 2 2 2 0 2 2 2 2 2 2 2 2 2 2 2 2 0 2 2 2 2 2 2 2 0 2 2 2 2 2 2 2 2 2 2
 2 2 2 2 2 0 2 0 2 2 2 2 2 2 2 0 2 2 2 2 2 2 2 2 2 0 0 2 2 2 2 2 2 2 2 0 2
 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 0 2 2 2 2 2 2 2 2 0 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 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 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 1 1 1 1 1 1 0 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]</p>
<pre><code>
- `np.unique`: 각 레이블에 몇개씩 묶여있는지 확인
- `return_counts`: 고유값의 개수를 보여주는 옵션 매개변수
</code></pre><p>print(np.unique(km.labels_, return_counts=True))</p>
<p>-&gt;
(array([0, 1, 2], dtype=int32), array([112,  98,  90]))</p>
<pre><code>
### 이미지 출력
- 클러스터에 들어있는 이미지를 그려주는 함수 만들기

- `draw_fruits`: arr에 3차원 배열을 입력 받아서 그림으로 출력해줄 함수
- `squeeze = False`: axs를 항상 2차원 배열로 다루기 위해서 설정
</code></pre><p>import matplotlib.pyplot as plt</p>
<p>def draw_fruits(arr, ratio=1): # 전달값 arr, ratio는 figsize때문에 설정
    n = len(arr)    # n은 샘플 개수
    # 한 줄에 10개씩 이미지를 그림. 샘플 개수를 10으로 나누어 전체 행 개수를 계산
    rows = int(np.ceil(n/10))
    # 행이 1개 이면 열 개수는 샘플 개수 그렇지 않으면 10개
    cols = n if rows &lt; 2 else 10
    fig, axs = plt.subplots(rows, cols,
                            figsize=(cols<em>ratio, rows</em>ratio), squeeze=False)
    for i in range(rows):
        for j in range(cols):
            if i<em>10 + j &lt; n:    # n 개까지만 그립니다.
                axs[i, j].imshow(arr[i</em>10 + j], cmap=&#39;gray_r&#39;)
                # [i, j] 위치에 전달받은 arr의 i*10+j번째 샘플을 그려라
            axs[i, j].axis(&#39;off&#39;) # 테두리 끄기
    plt.show()</p>
<pre><code></code></pre><p>draw_fruits(fruits[km.labels_==0])</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/e9b9dcc5-2f0d-4ed5-b60a-1855767ac8fe/image.png)
</code></pre><p>draw_fruits(fruits[km.labels_==1])</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/cef9b984-d229-42f3-bf2e-d6f7ad28eb09/image.png)

### 클러스터 중심 활용
- 위에서 &#39;클러스터 중심(=샘플 평균값)&#39; -&gt; `.cluster_centers_`에 저장되어 있음
- 3차원 배열로 다시 바꿔서 그림으로 나타내보면 &#39;평균 이미지(대표 이미지)&#39;가 나옴
</code></pre><p>draw_fruits(km.cluster_centers_.reshape(-1, 100, 100), ratio=3)</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/2ea8ebf6-bff0-44f5-9713-912986e4a93f/image.png)

-`.transform`: KMeans의 메소드 중 하나, 특정 샘플을 넣으면 그 샘플과 클러스터 중심들까지의 거리 반환
- 10000개(픽셀)의 특성을 3개(거리)의 특성으로 차원을 줄여주는 변환기로 사용 가능
</code></pre><p>print(km.transform(fruits_2d[100:101]))</p>
<p>-&gt;
[[3400.24197319 8837.37750892 5279.33763699]]</p>
<pre><code>
- `.predict()`: 위 거리를 이용해 예측 클래스를 출력하는 메소드
</code></pre><h1 id="위에서-구한-거리가-가장-짧은-것이-그-샘플이-속하는-클러스터">위에서 구한 거리가 가장 짧은 것이 그 샘플이 속하는 클러스터</h1>
<p>print(km.predict(fruits_2d[100:101]))</p>
<p>-&gt;
[0]</p>
<pre><code>
- `.n_iter_`: 클러스터 중심을 옮기는 &#39;반복횟수&#39;가 저장된 속성
</code></pre><h1 id="실제로-그려보면-예측한-클러스터-0파인애플이-맞음">실제로 그려보면 예측한 클러스터 0(파인애플)이 맞음</h1>
<p>draw_fruits(fruits[100:101])</p>
<h1 id="중심을-옮기면서-찾는-과정-반복-횟수--4">중심을 옮기면서 찾는 과정 반복 횟수 = 4</h1>
<p>print(km.n_iter_)</p>
<p>-&gt;
4</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/5b0d61e7-75df-4162-90fb-45b6bd40ef1a/image.png)

➕참고로, .transform()과 .predict()는 다른 군집 알고리즘엔 잘 없음. KMeans의 특징적인 메소드임...!

## 최적의 K 찾기
### k-평균 알고리즘의 단점
- 종류가 몇개인기 알기 때문에 n_cluster=3으로 fit함
    -&gt; 몇 개인지 모르는 상황에서는 클러스터 개수를 몇 개로 할지 판단 어려움
- 적절한 k값 찾기가 중요함

### 엘보우(elbow) 방법
1) 앞에서 `.transform()`으로 구했던 &#39;거리&#39;의 제곱 합을 이너셔(inertia)라 함
2) 이 이너셔 값이 작다는 것은, 클러스터 중심에 샘플들이 조밀하게 잘 모여있다고 해석할 수 있음. (= 작을수록 좋다고 볼 수 있음)
3) 보통 클러스터 개수(k)를 늘리면, 더 곳곳에 센트로이드가 자리하니까 이너셔가 줄어듦.


- 이 원리를 활용해, 클러스터 개수를 바꿔가면서 이너셔 값의 변화를 그래프로 살펴보면, 보통 감소 속도가 꺾이는 지점이 있기 마련 ➡️ 그 지점을 최적의 k로 채택
- `.inertia_` : KMeans에서 자동으로 계산한 이너셔 값
</code></pre><h1 id="inertial-값은-inertia_에-계산되어-있음">inertial 값은 inertia_에 계산되어 있음</h1>
<p>inertia = []
for k in range(2, 7):
    km = KMeans(n_clusters=k, n_init=&#39;auto&#39;, random_state=42)
    km.fit(fruits_2d)
    inertia.append(km.inertia_)</p>
<p>plt.plot(range(2, 7), inertia)
plt.xlabel(&#39;k&#39;)
plt.ylabel(&#39;inertia&#39;)
plt.show()</p>
<h1 id="꺽이는-지점에서-n_cluster보다-늘려도-inertia-더-안-낮아지는-경우가-많음">꺽이는 지점에서 n_cluster보다 늘려도 inertia 더 안 낮아지는 경우가 많음,</h1>
<h1 id="그래서-그-정도를-저걸한-k라고-봄">그래서 그 정도를 저걸한 k라고 봄</h1>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/7a62857c-8021-4efa-bd32-6769f15de785/image.png)

=&gt; 이 방법도 명확하진 않아서 다른 지도학습이나 문제에 적용하면서 후작업을 진행하고 그 결과를 바탕으로 피드백하면서 모델을 개선하는 경우가 많음


# 06-3 주성분 분석
## [키워드]
### 차원 축소
: 원본 데이터의 특성을 적은 수의 새로운 특성으로 변환하는 비지도 학습의 한 종류
- 저장 공간을 줄이고 시각화하기 쉬움
- 다른 알고리즘의 성능을 높일 수 있음

### 주성분 분석
: 차원 축소 알고리즘의 하나로 데이터에서 가장 분산이 큰 방향을 찾는 방법
- 원본 데이터를 주성분에 투영하여 새로운 특성을 만들 수 있음
- 일반적으로 주성분은 원본 데이터에 있는 특성 개수보다 작음

### 설명된 분산
: 주성분 분석에서 주성분이 얼마나 원본 데이터의 분산을 달 나타내는지 기록한 것
- 사이킷런의 PCA 클래스는 주성분 개수나 설명된 분산의 비율을 지정하여 주성분 분석을 수행가능

## 차원과 차원 축소
### 차원
: 데이터가 가진 속성 =&gt; 특성, 머신러닝에선 이 &#39;특성&#39;을 &#39;차원&#39;이라고 함

&gt; n차원 배열과 1차원 배열에서 용어가 다름
![](https://velog.velcdn.com/images/h_rin/post/99b16ab6-32bd-4316-8b0e-21dbf85abc9a/image.png)
- n차원 배열: 차원 = 축의 개수
- 1차원 배열(벡터): 차원 = 원소으 ㅣ개수

=&gt; &#39;차원 축소&#39;에서 축소하는 차원은 벡터로서의 차원(1차원 배열)

### 차원 축소

- 비지도학습의 한 종류로, 데이터를 가장 잘 나타내는 일부 특성을 선택하여 데이터 크기를 줄이고 다른 알고리즘의 성능을 향상시킬 수 있는 방법
    - 특성 개수가 많아지면 과대적합되는데, 이를 막아줌
    - 특성 개수가 줄어드니까 당연히 용량도 줄여줌.
    - 시각화하기도 쉬워짐.

- 반대로, 줄어든 차원에서 원본 차원으로 복원할 수도 있음
- 대표적으로 주성분 분석(Principal Component Analysis)이 있음

## 주성분 분석(PCA)
### 첫번째 주성분
- 직관적으로 길게 늘어진 대각선 방향이 분산이 가장 크다고 불 수 있음
    (= 가장 많이 퍼져있는 방향 = 데이터를 가장 잘 표현하는 방향)
![](https://velog.velcdn.com/images/h_rin/post/ebb005d9-6778-460f-8abd-f9f52d26b374/image.png)

- 위 직선을 원점으로 옮기면 벡터로 표현하기 쉬워지고, 이 벡터 (2,1)이 주성분이 됨
- 특성은 X1, X2 로 2개였는데 ➡️ (2,1) 이라는 1개의 데이터로 표현가능
![](https://velog.velcdn.com/images/h_rin/post/7a176fa1-cee8-4d94-b0f9-66167470d577/image.png)

- 주성분(벡터)을 활용하면, 원본데이터의 차원을 줄일 수 있음
ex)  s(4,2)라는 2차원 샘플을 &quot;주성분에 투영하면&quot; 1차원 데이터 p(4.5)로 만듦
![](https://velog.velcdn.com/images/h_rin/post/d558e5d7-dd65-4a43-b341-23e932d75ceb/image.png)

❗ 주성분을 원본차원과 같고 주성분으로 바꾼 데이터는 차원이 줄어들음


### 두번째 주성분
- 주성분은 원본의 특성 개수만큼 찾을 수 있음
(ex. 현재 그래프에서 X1, X2로 2차원이기 때문에 주성분 최대 2개까지 찾을 수 있음)
- 첫 번째 주성분에 수직이면서, 분산이 가장 큰 다음 방향을 찾으면 됨
(첫번째 주성분이 표현하지 못하는 분산의 방향을 찾는 것이 효율적이기 때문)
![](https://velog.velcdn.com/images/h_rin/post/5e68993e-f356-4d5c-8532-5b3137d45a92/image.png)

=&gt; 원본 특성이 2개인데 주성분 2개를 다 찾으면 차원 축소가 안되는 셈이기 때문에 보통은 더 적은 개수의 주성분만 찾아서 활용

## PCA 클래스
### 주성분 분석
- 이전 차시와 동일하게 데이터 준비</code></pre><p>!wget <a href="https://bit.ly/fruits_300_data">https://bit.ly/fruits_300_data</a> -O fruits_300.npy</p>
<p>import numpy as np</p>
<p>fruits = np.load(&#39;fruits_300.npy&#39;)
fruits_2d = fruits.reshape(-1, 100*100)</p>
<pre><code>
- 비지도 학습이기 때문에 입력 데이터만 전달해서 모델 훈련
- `PCA()`: 사이킷런에서 주성분 분석 알고리즘이 구현되어있는 클래스
- `n_components`: 찾을 주성분의 개수를 지정하는 매개변수</code></pre><p>from sklearn.decomposition import PCA</p>
<p>pca = PCA(n_components=50)
pca.fit(fruits_2d)</p>
<pre><code>
- `.components_`: 주성분이 저장되어 있음</code></pre><p>print(pca.components_.shape)</p>
<h1 id="찾은-주성분의-크기를-확인">찾은 주성분의 크기를 확인</h1>
<h1 id="--50개의-주성분-각-주성분들은-원소를-10000개씩-갖고-있음">-&gt; 50개의 주성분, 각 주성분들은 원소를 10000개씩 갖고 있음</h1>
<p>-&gt;
(50, 10000)</p>
<pre><code>
- 찾은 주성분 -&gt; 이미지로 출력, 원본 데이터셋의 어떤 패턴을 읽은 것으로 보임</code></pre><h1 id="100x100으로-바꿔서">100x100으로 바꿔서</h1>
<p>draw_fruits(pca.components_.reshape(-1, 100, 100))</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/b78dd41e-8060-4175-b7e5-4f08eb9b7911/image.png)

- `.transform()`: 전달받은 데이터를 주성분으로 분해(차원 축소)하는 메소드</code></pre><p>print(fruits_2d.shape) # 300개 샘플, 각각 10000개 특성</p>
<p>fruits_pca = pca.transform(fruits_2d) # 차원 축소
print(fruits_pca.shape) # 특성 개수 50개</p>
<p>-&gt;
(300, 10000)
(300, 50)</p>
<pre><code>
## 원본 데이터 재구성
- (10000 -&gt; 50), 반대로 원본 데이터로 복원(50 -&gt; 10000) 가능
    (압축 시 손실o -&gt; 100% 복구 x)
- `.inverse_transform()`: 주성분을 바탕으로 원본 데이터를 재구성하는 메소드</code></pre><p>fruits_inverse = pca.inverse_transform(fruits_pca)
print(fruits_inverse.shape)</p>
<p>-&gt;
(300, 10000)</p>
<pre><code>
- 그림으로 출력, 거의 모든 과일이 복원됨(50 -&gt; 10000, 일부 흐리거나 번진 부분 o)</code></pre><p>fruits_reconstruct = fruits_inverse.reshape(-1, 100, 100)</p>
<p>for start in [0, 100, 200]:
    draw_fruits(faruits_reconstruct[start:start+100])
    print(&quot;\n&quot;)</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/179a88cd-f048-4cc6-a76e-d23ea82d593f/image.png)
![](https://velog.velcdn.com/images/h_rin/post/7a54e4d1-fd72-47eb-b58d-3644a165f8a8/image.png)
![](https://velog.velcdn.com/images/h_rin/post/84d21d3a-5737-49ec-991b-fd34b51c4e41/image.png)

=&gt; 50개의 주성분이 원본의 분산을 매우 잘 나타내는 것을 의미

### 설명된 분산(explained variance)
: 주성분이 원본 데이터의 분산을 얼마나 잘 나타내는지 기록한 값
- `.explained_variance_ratio_`: 각 주성분의 설명된 분산 비율이 저장됨
    → 이 값을 sum으로 다 더하면 주성분 50개가 설명하는 총 분산 비율을 얻을 수 있음</code></pre><h1 id="비율-50개를-다-더함--총-분산-비율">비율 50개를 다 더함 = 총 분산 비율</h1>
<p>print(np.sum(pca.explained_variance_ratio_))</p>
<p>-&gt;
0.9215275787736402 (92%)</p>
<pre><code>
- 설명된 분산 비율 그래프로 그림, 적절한 주성분 개수를 찾는데 도움됨</code></pre><p>plt.plot(pca.explained_variance_ratio_)
plt.xlabel(&quot;pca&quot;)
plt.ylabel(&quot;explained variance ratio&quot;)
plt.show()</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/dff68ee7-7e65-4fc5-bf49-5b092cf3a587/image.png)
=&gt; 처음 10개가 주성분을 가장 많이 설명, 주요했음을 알 수 있음

## 다른 알고리즘과 함께 사용하기
PCA는 주로 다른 알고리즘과 연계하여 많이 사용

### 분류 알고리즘 (로지스틱 회귀)
- 모델을 만들고 타겟값(사과 0/ 파인애플 1/ 바나나 2)을 설정</code></pre><p>from sklearn.linear_model import LogisticRegression</p>
<p>lr = LogisticRegression()</p>
<p>target = np.array([0] * 100 + [1] * 100 + [2] * 100) </p>
<h1 id="지도학습-모델이기-때문에-임의로-타겟값-만들어줌">지도학습 모델이기 때문에 임의로 타겟값 만들어줌</h1>
<pre><code>
- 원본 데이터로 훈련해서 교차검증</code></pre><p>from sklearn.model_selection import cross_validate
scores = cross_validate(lr, fruits_2d, target)</p>
<h1 id="로지스틱-객체에-원본과-타깃-전달">로지스틱 객체에 원본과 타깃 전달</h1>
<p>print(np.mean(scores[&#39;test_score&#39;])) # 점수 출력
print(np.mean(scores[&#39;fit_time&#39;])) # 훈련하는데 걸린 시간 출력</p>
<p>-&gt;
0.9966666666666667
1.7471996784210204</p>
<pre><code>
- PCA 차원 축소한 데이터</code></pre><p>scores = cross_validate(lr, fruits_pca, target)</p>
<h1 id="입력-데이터만-바꿈">입력 데이터만 바꿈</h1>
<p>print(np.mean(scores[&#39;test_score&#39;]))
print(np.mean(scores[&#39;fit_time&#39;]))</p>
<p>-&gt;
1.0
0.04023046493530273</p>
<pre><code>=&gt; 특성 50개만 쓰면서 (성능은 유지) 훈련시간 감소

### 군집 알고리즘(k-평균)
- 모델을 만들고, (비지도학습이니까) 타깃값 없이 훈련
</code></pre><p>from sklearn.cluster import KMeans</p>
<p>km = KMeans(n_clusters=3, random_state=42) # KMeans 모델 만듦
km.fit(fruits_pca) # 2개의 특성 축소해놓은 데이터 fit</p>
<p>print(np.unique(km.labels_, return_counts=True))</p>
<h1 id="구해진-클러스터-각각-몇-개씩-묶였나-확인">구해진 클러스터, 각각 몇 개씩 묶였나 확인</h1>
<p>-&gt;
(array([0, 1, 2], dtype=int32), array([110,  99,  91]))</p>
<pre><code>
- 이미지로 출력해봄</code></pre><p>for label in range(0, 3):
    draw_fruits(fruits[km.labels_ == label])
    print(&quot;\n&quot;)</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/c2b41f16-6fc2-41ef-ab3b-995b03121a30/image.png)
![](https://velog.velcdn.com/images/h_rin/post/78407326-4dd5-4f02-9817-111b71d592c9/image.png)
![](https://velog.velcdn.com/images/h_rin/post/30852087-4d3f-4b42-93d1-2c801733241c/image.png)

### 데이터 시각화
- 데이터를 차원축소 -&gt; 시각화 가능
- 특성 2개 → `fruits_pca` → 3개의 과일 산점도
</code></pre><p>for label in range(0, 3):
    data = fruits_pca[km.labels_ == label]
    plt.scatter(data[:,0], data[:,1])
plt.legend([&#39;pineapple&#39;, &#39;banana&#39;, &#39;apple&#39;])
plt.show()</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/10f7cbeb-705a-4182-8594-d97417fa339e/image.png)
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[MySQL 한 번에 끝내기]]></title>
            <link>https://velog.io/@h_rin/MySQL-%ED%95%9C-%EB%B2%88%EC%97%90-%EB%81%9D%EB%82%B4%EA%B8%B0</link>
            <guid>https://velog.io/@h_rin/MySQL-%ED%95%9C-%EB%B2%88%EC%97%90-%EB%81%9D%EB%82%B4%EA%B8%B0</guid>
            <pubDate>Mon, 11 Nov 2024 12:55:08 GMT</pubDate>
            <description><![CDATA[<h1 id="mysql이란">MySQL이란?</h1>
<ul>
<li>가장 널리 사용되고 있는 관계형 데이터베이스 관리 시스템(RDBMS:Relational DBMS)</li>
<li>오픈소스임, 다중 사용자와 다중 스레드 지원</li>
<li>여러 프로그래밍 언어를 위한 다양한 API 제공</li>
<li>유닉스, 리눅스, 윈도우 등 다양한 운영체제에서 사용가능
  -&gt; PHP와 함께 웹 개발에서 자주 사용</li>
</ul>
<h1 id="sql-기본">SQL 기본</h1>
<h2 id="sql의-분류">SQL의 분류</h2>
<p>[DML(Data Manipulation Language)]</p>
<ul>
<li>데이터 조작 언어</li>
<li>데이터를 조작(선택, 삽입, 수정, 삭제)하는데 사용되는 언어</li>
<li>DML 구문이 사용되는 대상은 테이블의 행</li>
<li>DML 사용하기 위해서는 꼭 이전에 테이블이 정의되어 있어야 함</li>
<li>SELECT, INSERT, UPDATE, DELETE가 이 구문에 해당함</li>
<li>트랜잭션(Transection)이 발생하는 SQL도 이 DML에 속함<ul>
<li>테이블의 데이를 변경(입력/수정/삭제)할 때 실제 테이블에 완전히 적용하지 않고 임시로 적용시키는 것</li>
<li>취소 가능함</li>
</ul>
</li>
</ul>
<p>[DDL(Data Definition Language)]</p>
<ul>
<li>데이터 정의 언어</li>
<li>데이터베이스, 테이블, 뷰, 인덱스 등의 데이터 베이스 개체를 생성/삭제/변경하는 역할</li>
<li>CREATE, DROP, ALTER 구문</li>
<li>트랜잭션 발생X</li>
<li>ROLLBACK이나 COMMIT 사용 불가</li>
<li>실행 즉시 MySQL에 적용</li>
</ul>
<p>[DCL(Data Control Language)]</p>
<ul>
<li>데이터 제어 언어</li>
<li>사용자에게 어떤 권한을 부여하거나 빼앗음</li>
<li>GRANT,REVOTE,DENY 구문</li>
</ul>
<h4 id="show-databases">SHOW DATABASES</h4>
<ul>
<li>현재 서버에 어떤 DB가 있는지 확인<pre><code>SHOW DATABASES;</code></pre></li>
</ul>
<h4 id="use">USE</h4>
<ul>
<li>사용할 데이터베이스 지정</li>
<li>지정해 놓은 후 USE문을 다시 사용하지 않는 이상 처음 선택한 DB에서 SQL문 수행<blockquote>
<p>USE database_name</p>
</blockquote>
</li>
</ul>
<pre><code>USE world</code></pre><h4 id="show-table">SHOW TABLE</h4>
<ul>
<li>데이터 베이스의 테이블 이름 보기<pre><code>SHOW TABLES;</code></pre></li>
</ul>
<h4 id="show-table-status">SHOW TABLE STATUS</h4>
<ul>
<li>데이터베이스의 테이블 정보 조회<pre><code>SHOW TABLE STATUS;</code></pre></li>
</ul>
<h4 id="describedesc">DESCRIBE(DESC)</h4>
<ul>
<li>특정 테이블에 무슨 열이 있는지 확인<pre><code>DESCRIBE city;
</code></pre></li>
</ul>
<p>or</p>
<p>DESC city;</p>
<pre><code>
### [Lab 01]country 테이블과 countrylanguage 테이블 정보 보기
#### SELECT
- &lt;SELECT...FROM&gt;
- 요구하는 데이터를 가져오는 구문
- 가장 많이 쓰는 구문
- 데이터베이스 내 테이블에서 원하는 정보 추출

&gt; SELECT select_expr
    [FROM table_references]
    [WHERE where_condition]
    [GROUP BY {col_name | expr | position}]
    [HAVING where_condition]
    [ORDER BY {col_name | expr | position}]

[SELECT *]</code></pre><p>SELECT * FROM city</p>
<pre><code>
[SELECT 열 이름]
- 테이블에서 필요로 하는 열만 가져오는 기능
- 여러 개의 열을 가져오고 싶을 때는 콤마로 구분
- 열 이름의 순서는 출력하고 싶은 순서대로 배열 가능
</code></pre><p>SELECT Name, District FROM city;</p>
<pre><code>
#### SELECT FROM WHERE
[기본적인 WHERE절]
- 조회하는 결과에 특절한 조건으로 원하는 데이터만 보고 싶을 때 사용
- 조건이 없을 경우, 테이블의 크기가 클수록 찾는 시간과 노력 증가
&gt; SELECT 필드 이름 FROM 테이블 이름
  WHERE 조건식;

[관계 연산자 사용]
- 조건 연산자(=, &lt;, &gt;, &lt;=, &gt;=, &lt;&gt;, != 등)
- 관계 연산자(NOT, AND, OR 등)
- 연산자의 조합으로 데이터를 효율적으로 추출
</code></pre><p>SELECT *
FROM city
WHERE Population &gt; 8000000 AND Population &lt; 9000000</p>
<pre><code>
### [Lab 02] 한국에 있는 도시들 보기, 한국에 있는 도시들 중 인구 수가 1,000,000이상인 도시 보기
#### BETWEEN
- 데이터가 숫자로 구성되어 있어 연속적인 값
    =&gt; BTWEEN...AND 사용가능
</code></pre><p>SELECT *
FROM city
WHERE Population BTWEEN 7000000 AND 8000000</p>
<pre><code>
#### IN
- 이산적인 Discrete 값의 조건에서는 IN() 사용가능
</code></pre><p>SELECT *
FROM city
WHERE Name IN(&#39;Seoul&#39;, &#39;New York&#39;, &#39;Tokyo&#39;)</p>
<pre><code>
### [Lab 03] 한국, 미국, 일본의 도시들 보기
#### LIKE
- 문자열의 내용 검색하기 위해 LIKE 연산자 사용
- 문자뒤에 % =&gt; 무엇이든(%) 허용
- 한 글자와 매치하기 위해서 =&gt; &#39;_&#39; 사용
</code></pre><p>SELECT *
FROM city
WHERE CountryCode LIKE &#39;KO_&#39;</p>
<pre><code>
#### Sub Query
- 서브쿼리
- 쿼리문 안에 또 쿼리문이 들어 있는 것
- 서브 쿼리의 결과가 둘 이상이 되면 에러 발생
</code></pre><p>SELECT *
FROM city
WHERE CountryCode = (     SELECT CountryCode
                        FROM city
                        WHERE NAMe = &#39;Seoul&#39;    );</p>
<pre><code>
#### ANY 
- 서브쿼리의 여러 개의 결과 중 한 가지만 만족해도 사용가능
- SOME은 ANY와 동일한 의미로 사용
- =ANY 구문은 IN과 동일한 의미
</code></pre><p>SELECT *
FROM city
WHERE Population &gt; ANY (    SELECT Population
                            FROM city
                            WHERE District = &#39;New York&#39;    );</p>
<pre><code>
#### ALL
- 서브쿼리의 여러 개의 결과를 모두 만족 시켜야 함
</code></pre><p>SELECT *
FROM city
WHERE Population &gt; ALL (    SELECT Population
                            FROM city
                            WHERE District = &#39;New York&#39;    );</p>
<pre><code>
#### ORDER BY
-결과가 출력되는 순서를 조절하는 구문
- 기본적으로 오름차순(ASCENDING)정렬
- 내림차순(DESCENDING)으로 정렬
    - 열 이름뒤에 DESC 적어야됨
- ASC(오름차순) 는 default이므로 생략 가능
</code></pre><p>SELECT *
FROM city
ORDER BY Population DESC</p>
<pre><code>
- ORDER BY 구문을 혼합해 사용하는 구문도 가능
</code></pre><p>SELECT *
FROM city
ORDER BY CountryCode ASC, Population DESC</p>
<pre><code>
### [Lab 04] 인구수로 내림차순하여 한국에 있는 도시 보기, 국가 면적 크기로 내림차순하여 나라 보기(country table)
#### DISTINCT
- 중복된 것은 1개씩만 보여주면서 출력
- 테이블의 크기가 클수록 효율적
</code></pre><p>SELECT DISTINCT CountryCode
FROM city</p>
<pre><code>
#### LIMIT
- 출력 개수 제한
- 상위의 N개만 출력하는 &#39;LIMIT N&#39;구문
- 서버의 처리량을 많이 사용해 서버의 전반적인 성능을 나쁘게 하는 악성 쿼리문을 개선할 때 사용
</code></pre><p>SELECT *
FROM city
ORDER BY Population DESC
LIMIT 10</p>
<pre><code>
#### GROUP BY
- 그룹으로 묶어주는 역할
- 집계 함수(Aggregate Function)를 함께 사용
    - AVG() : 평균
    - MIN() : 최소값
    - MAX() : 최대값
    - COUNT() : 행의 개수
    - COUNT(DISTINCT) : 중복 제외된 행의 개수
    - STDEV() : 표준편차
    - VARIANCE() : 분석
- 효율적인 데이터 그룹화(Grouping)
- 읽기 좋게 하기 위해 별칭(Alias) 사용
![](https://velog.velcdn.com/images/h_rin/post/a7e71a60-89d2-409d-acf1-ec030dc6b6e5/image.png)
</code></pre><p>SELECT CountryCode, MAX(Population)
FROM city
GROUP BY CountryCode</p>
<pre><code>
### [Lab 05] 도시는 몇개인가?, 도시들의 평균 인구수는?
#### HAVING
- WHERE과 비슷한 개념으로 조건 제한
- 집계 함수에 대해서 조건 제한하는 편리한 개념
- HAVING절은 반드시 GROUP BY절 다음에 나와야 함
</code></pre><p>SELECT CountryCode, MAX(Population) &#39;MAX Population&#39;
FROM city
GROUP BY CountryCode
HAVING MAX(Population) &gt; 80000000</p>
<pre><code>
#### ROLLUP
- 총합 또는 중간합계가 필요할 경우 사용
- GRUOP BY절과 함께 WITH ROLLUP문 사용
</code></pre><p>SELECT CountryCode, Name, SUM(Population)
FROM city
GROUP BY CountryCode, Name WITH ROLLUP</p>
<pre><code>
#### JOIN
- JOIN은 데이터베이스 내의 여러 테이블에서 가져온 레코드를 조합하여 하나의 테이블이나 결과 집합으로 표현
</code></pre><p>SELECT * FROM city
JOIN country
ON city.CountryCode = country.Code</p>
<pre><code>
### [Lab 06] city, country, countrylanguage 테이블 3개를 JOIN하기
#### MySQL 내장함수
- 사용자의 편의를 위해 다양한 기능의 내장 함수를 미리 정의하여 제공
- 대표적인 내장 함수의 종류
    - 문자열 함수
    - 수학 함수
    - 날짜와 시간 함수

#### LENGTH()
- 전달받은 문자열의 길이를 반환
</code></pre><p>SELECT LENGTH(&#39;123456789&#39;);</p>
<pre><code>
#### CONCAT()
- 전달받은 문자열을 모두 결합하여 하나의 문자열로 반환
- 전달받은 문자열 중 하나라도 NULL이 존재하면 NULL을 반환
</code></pre><p>SELECT CONCAT(&#39;My&#39;, &#39;sql Op&#39;, &#39;en Source&#39;),
CONCAT(&#39;MySQL&#39;, NULL, &#39;OpenSource&#39;);</p>
<pre><code>
#### LOCATE()
- 문자열 내에서 찾는 문자열이 처음으로 나타나는 위치를 찾아서 해당 위치를 반환
- 찾는 문자열이 문자열 내에 존재하지 않으면 0을 반환
- MySQL에서는 문자열의 시작 인덱스를 1부터 계산
</code></pre><p>SELECT LOCATE(&#39;abc&#39;, &#39;abababcbacdABC&#39;),
LOCATE(&#39;abc&#39;, &#39;abababcbacdABC&#39;, 7);</p>
<pre><code>
#### LEFT(), RIGHT()
- LEFT(): 문자열의 왼쪽부터 지정한 개수만큼의 문자를 반환
- RIGHT(): 문자열의 오른쪽부터 지정한 개수만큼의 문자를 반환
</code></pre><p>SELECT
LEFT(&#39;MySQL is an open source relational database management system&#39;, 5), RIGHT(&#39;MySQL is an open source relational database management system&#39;, 6);</p>
<pre><code>
#### LOWER(), UPPER()
- LOWER(): 문자열의 문자를 모두 소문자로 변경
- UPPER(): 문자열의 문자를 모두 대문자로 변경
</code></pre><p>SELECT
LOWER(&#39;MySQL is an open source relational database management system&#39;), UPPER(&#39;MySQL is an open source relational database management system&#39;);</p>
<pre><code>
#### REPLACE()
- 문자열에서 특정 무나열을 대체 문자열로 교체
</code></pre><p>SELECT REPLACE(&#39;MSSQL&#39;, &#39;MS&#39;, &#39;My&#39;);</p>
<pre><code>
#### TRIM()
- 문자열의 앞이나 뒤, 또는 양쪽 모두에 있는 특정 문자를 제거
- TRIM() 함수에서 사용할 수 있는 지정자
    - BOTH: 전달받은 문자열의 양 끝에 존재하는 특정 문자를 제거(기본 설정)
    - LEADING: 전달받은 문자열 앞에 존재하는 특정 문자를 제거
    - TRAILING: 전달받은 문자열 뒤에 존재하는 특정 문자를 제거
- 만약 지정자를 명시하지 않으면 자동으로 BOTH로 설정
- 제거할 문자를 명시하지 않으면 자동으로 공백을 제거
</code></pre><p>SELECT TRIM(&#39;    ##My SQL##    &#39;),
TRIM(LEADING &#39;#&#39; FROM &#39;##MySQL##&#39;),
TRIM(TRAILING &#39;#&#39; FROM &#39;##MySQL##&#39;);</p>
<pre><code>
#### FORMAT()
- 숫자 타입의 데이터를 세 자리마다 쉼표(,)를 사용하는 &#39;#, ###, ###.##&#39;형식으로 변환
- 반환되는 데이터의 형식은 문자열 타입
- 두 번째 인수는 반올림할 소수 부분의 자릿수
</code></pre><p>SELECT FORMAT(123456789.123456, 3);</p>
<pre><code>
#### FLOOR(), CEIL(), ROUNT()
- FLOOR(): 내림
- CEIL(): 올림
- ROUND(): 반올림

![](https://velog.velcdn.com/images/h_rin/post/8bd006f3-6c46-49da-9317-88c010fcee57/image.png)

#### SQRT(), POW(), EXP(), LOG()
- SQRT(): 양의 제곱근
- POW(): 첫 번째 인수로 밑수를 전달하고, 두 번째 인수로 지수를 전달하여 거듭제곱 계산
- EXP(): 인수로 지수를 전달받아, e의 거듭제곱을 계산
- LOG(): 자연로그 값을 계산

![](https://velog.velcdn.com/images/h_rin/post/0813a5bc-dd98-4a7d-b16c-04a09bdb3330/image.png)

#### SIN(), COS(), TAN()
- SIN(): 사인값 반환
- COS(): 코사인값 반환
- TAN(): 탄젠트값 반환

![](https://velog.velcdn.com/images/h_rin/post/4d936b0a-24d5-4bc8-846c-8afabde9f289/image.png)

#### ABS(), RAND()
- ABS(X): 절대값 반환
- RAND(): 0.0보다 크거나 같고 1.0보타 작은 하나의 실수를 무작위로 생성

![](https://velog.velcdn.com/images/h_rin/post/fc04f5c5-482a-4bb4-a2f1-415dbe08c7d2/image.png)

#### NOW(), CURDATE(), CURTIME()
- NOW(): 현재 날짜와 시간을 반환, 반환되는 값은 &#39;YYYY-MM-DD HH:MM:SS&#39; OR YYYYMMDDHHMMSS 형태로 반환
- CURDATE(): 현재 날짜를 반환, 이때 반환되는 값은 &#39;YYYY-MM-DD&#39;OR YYYYMMDD 형태로 반환
- CURTIME(): 현재 시각을 반환, 이때 반환되는 값은 &#39;HH:MM:SS&#39; OR HHMMSS 형태로 반환

![](https://velog.velcdn.com/images/h_rin/post/2fcfc890-35a8-4fd5-8c7a-040d83caa387/image.png)

#### DATE(), MONTH(), DAY(), HOUR(), MINUTE(), SECOND()
- DATE(): 전달받은 값에 해당하는 날짜 정보를 반환
- MONTH(): 월에 해당하는 값을 반환하며, 0-12사이의 값
- DAY(): 일에 해당하는 값을 반환하며, 0-31사이의 값
- HOUR(): 시간에 해당하는 값을 반환, 0-23사이의 값
- MINUTE(): 분에 해당하는 값을 반환, 0-59사이의 값
- SECOND(): 초에 해당하는 값을 반환, 0-59사이의 값

![](https://velog.velcdn.com/images/h_rin/post/ec9cc00f-59e0-4332-879f-58421a238037/image.png)


#### MONTHNAME(), DAYNAME()
- MONTHNAME(): 월에 해당하는 이름을 반환
- DAYNAME(): 요일에 해당하는 이름을 반환

![](https://velog.velcdn.com/images/h_rin/post/c3fc5e8c-c21b-4c7e-af24-8258da1118e6/image.png)


#### DAYOFWEEK(), DAYOFMONTH(), DAYOFYEAR()
- DAYOFWEEK(): 일자가 해당 주에서 몇 번째 날인지를 반환, 1부터 7 사이의 값 반환 (일요일 = 1, 토요일 = 7)
- DAYOFMONTH(): 일자가 해당 월에서 몇 번째 날인지를 반환, 0부터 31 사이의 값 반환
- DAYOFYEAR(): 일자가 해당 연도에서 몇 번째 날인지를 반환, 1부터 366 사이의 값 반환

![](https://velog.velcdn.com/images/h_rin/post/7b656c79-40c8-4c41-b8d7-35a1e9db3020/image.png)

#### DATE_FORMAT()
- 전달받은 형식에 맞춰 날짜와 시간 정보를 문자열로 반환
- [MySQL Date and Time Function](https://dev.mysql.com/doc/refman/8.0 /en/date-and-time-functions.html )

![](https://velog.velcdn.com/images/h_rin/post/1058dca0-f90d-49d2-b628-b5053115996a/image.png)


# SQL 고급
### CREATE TABLE AS SELECT
- city 테이블과 똑같은 city2 테이블 생성

![](https://velog.velcdn.com/images/h_rin/post/6e63c55a-2153-460c-b4f9-790b2b689bca/image.png)

### CREATE DATBASE
- CREATE DATBASE문은 새로운 데이터베이스 생성
- USE문으로 새 데이터베이스 사용

![](https://velog.velcdn.com/images/h_rin/post/727e990e-77e3-42fe-81a7-7e0b640c20dd/image.png)

## CREATE TABLE (MySQL Workbench)
- [데이터 타입](https://dev.mysql.com/doc/refman/8.0 /en/data-types.html)

![](https://velog.velcdn.com/images/h_rin/post/428541de-7f37-489b-973d-2e6007223cab/image.png)

![](https://velog.velcdn.com/images/h_rin/post/582cad4b-92e0-425b-86ae-d723b1875310/image.png)

![](https://velog.velcdn.com/images/h_rin/post/dab92a59-701b-414c-bae5-d358803f4943/image.png)

![](https://velog.velcdn.com/images/h_rin/post/b30e77cd-b0c6-4773-8f92-4f7e2b9d4f5e/image.png)

### ALTER TABLE
- ALTER TABLE문과 함께 MODIFY문을 사용하면, 테이블의 컬럼 타입을 변경할 수 있음
![](https://velog.velcdn.com/images/h_rin/post/6630cfbf-69a1-4dac-adb8-6b921d96b3f0/image.png)

- ALTER TABLE문과 함께 DROP문을 사용하면, 테이블에 열을 제거 가능
![](https://velog.velcdn.com/images/h_rin/post/bfbcf100-f3b1-4518-97e8-dfc00cfb34af/image.png)

## 인덱스(Index)
- 테이블에서 원하는 데이터를 빠르게 찾기 위해 사용
- 일반적으로 데이터를 검색할 때 순서대로 테이블 전체를 검색하므오 데이터가 많으면 많을수록 탐색하는 시간이 늘어남
- 검색과 질의를 할 때 테이블 전체를 읽지 않기 때문에 빠름
- 설정된 열의 값을 포함한 데이터의 삽입, 삭제, 수정 작업이 원본 테이블에서 이루어질 경우, 인덱스도 함께 수정되어야 함
- 인덱스가 있는 테이블은 처리 속도가 느려질 수 있으므로 수정보다는 검색이 자주 사용되는 테이블에서 사용하는 것이 좋음

### CREATE INDEX
- CREATE INDEX문을 사용하여 인덱스를 생성
</code></pre><p>CREATE INDEX Col1Idx
ON test (col1);</p>
<pre><code>
### SHOW INDEX
- 인덱스 정보 보기
</code></pre><p>SHOW INDEX
FROM test;</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/82094015-b9ec-40cb-bc78-8687985bfa81/image.png)


### CREATE UNIQUE INDEX
- 중복 값을 허용하지 않는 인덱스
</code></pre><p>CREATE UNIQUE INDEX Col2Idx
ON test (col2);</p>
<p>SHOW INDEX FROM test;</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/9d6eac89-adfd-4da2-b6d2-b231316a5ac8/image.png)

### FULLTEXT INDEX 
- FULLTEXT INDEX는 일반적인 인덱스와는 달리 매우 빠르게 테이블의 모든 텍스트 컬럼을 검색
</code></pre><p>ALTER TABLE test
ADD FULLTEXT Col3Idx(col3);</p>
<p>SHOW INDEX FROM test;</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/ee80fec5-01e3-4f6a-8888-ac0e85954309/image.png)

### INDEX 삭제 (ALTER)
- ALTER 문을 사용하여 테이블에 추가된 인덱스 삭제
</code></pre><p>ALTER TABLE test
DROP INDEX Col3Idx;</p>
<p>SHOW INDEX FROM test;</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/fa15bb10-d609-4923-82b3-57121d84b529/image.png)

### INDEX 삭제 (DROP INDEX)
- DROP 문을 사용하여 해당 테이블에서 명시된 인덱스를 삭제
- DROP 문은 내부적으로 ALTER 문으로 자동 변환되어 명시된 이름의 인덱스를 삭제
</code></pre><p>DROP INDEX Col2Idx ON test;</p>
<p>SHOW INDEX FROM test;</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/8ce2bfb7-9197-4663-b938-84f7a8aa2bbf/image.png)

## VIEW
- 뷰(view)는 데이터베이스에 존재하는 일종의 가상 테이블
- 실제 테이블처럼 행과 열을 가지고 있지만, 실제로 데이터를 저장하지 X
- MySQL에서 뷰는 다른 테이블이나 다른 뷰에 저장되어 있는 데이터를 보여주는 역할만 수행
- 뷰를 사용하면 여러 테이블이나 뷰를 하나의 테이블처럼 볼 수 있음

[view의 장점]
- 특정 사용자에게 테이블 전체가 아닌 필요한 컬럼만 보여줄 수 있음
- 복잡한 쿼리를 단순화해서 사용
- 쿼리 재사용 가능

[view의 단점]
- 한 번 정의된 뷰는 변경 x
- 삽입, 삭제, 갱신 작업에 많은 제한 사항을 가짐
- 자신만의 인덱스를 가질 수 x

### CREATE VIEW
- CREATE VIEW문을 사용하여 뷰 생성
</code></pre><p>CREATE VIEW testView AS
SELECT Col1, Col2
FROM test</p>
<p>SELECT * FROM testView;</p>
<pre><code>
![](https://velog.velcdn.com/images/h_rin/post/be47f4ed-be60-460b-996b-1837b26732a7/image.png)


### ALTER VIEW
- ALTER VIEW문을 사용하여 뷰를 수정
</code></pre><p>ALTER VIEW testView AS
SELECT Col1, Col2, Col3
FROM test;</p>
<p>SELECT * FROM testView;</p>
<pre><code>
![](https://velog.velcdn.com/images/h_rin/post/e3191058-61ae-4284-8e20-9f8d6cda1d8c/image.png)

### DROP VIEW
- DROP문을 사용하여 생성된 뷰를 삭제
</code></pre><p>DROP VIEW testView;</p>
<pre><code>
## [Lab 07] city, country, countrylanguage 테이블을 JOIN하고, 한국에 대한 정보만 뷰 생성하기

### INSERT
- 테이블 이름 다음에 나오는 열 생략 가능
- 생략할 경우에 VALUE 다음에 나오는 값들의 순서 및 개수가 테이블이 정의된 열 순서 및 개수와 동일해야 함
</code></pre><p>INSERT INTO test
VALUE(1, 123, 1.1, &quot;Test&quot;);</p>
<p>SELECT *
FROM test;</p>
<pre><code>
### INSERT (MySQL Workbench)
![](https://velog.velcdn.com/images/h_rin/post/98579420-952e-4ba2-8797-09cd1a5c8630/image.png)

### INSERT INTO SELECT
- test 테이블에 있는 내용을 test2 테이블에 삽입
</code></pre><p>INSERT INTO test2 SELECT * FROM test;</p>
<p>SELECT * FROM test2;</p>
<pre><code>
### UPDATE
- 기존에 입력되어 있는 값 변경하는 구문
- WHERE절 생략 가능하지만 테이블 전체 행의 내용 변경
</code></pre><p>UPDATE test
SET col1 = 1, col2 = 1.0, col3 = &#39;test&#39;
WHERE id = 1;</p>
<p>SELECT * FROM test;</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/d8c8042f-44ef-4ddf-8036-204b1127cbdc/image.png)

### DELETE
- 행 단위로 데이터 삭제하는 구문
- DELETE FROM 테이블 이름 WHERE 조건;
- 데이터는 지워지지만 테이블 용량은 줄어들지 x
- 원하는 데이터만 지울 수 있음
- 삭제 후 잘못 삭제한 것을 되돌릴 수 있음
</code></pre><p>DELETE FROM test
WHERE id = 1;</p>
<p>SELECT * FROM test;</p>
<pre><code>
![](https://velog.velcdn.com/images/h_rin/post/3ee3b52f-0c38-47b0-921f-cc25e39ff4a9/image.png)

### TRUNCATE
- 용량이 줄어 들고, 인덱스 등도 모두 삭제
- 테이블은 삭제하지 않고, 데이터만 삭제
- 한꺼번에 다 지워야 함
- 삭제 후 절대 되돌릴 수 없음
</code></pre><p>TRUNCATE TABLE test;</p>
<p>SELECT * FROM test;</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/c4a7102d-bb2a-4cf0-91bd-122a8f300086/image.png)

### DROP TABLE
- 테이블 전체를 삭제, 공간, 객체를 삭제
- 삭제 후 절대 되돌릴 수 없음
</code></pre><p>DROP TABLE test;</p>
<pre><code>
### DROP DATABASE
- DROP DATABASE문은 데이터베이스를 삭제
</code></pre><p>DROP DATABASE suan;</p>
<pre><code>







</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[혼자 공부하는 머신러닝  딥러닝]Chapter 05. 트리 알고리즘]]></title>
            <link>https://velog.io/@h_rin/%ED%98%BC%EC%9E%90-%EA%B3%B5%EB%B6%80%ED%95%98%EB%8A%94-%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-%EB%94%A5%EB%9F%AC%EB%8B%9DChapter-05.-%ED%8A%B8%EB%A6%AC-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98</link>
            <guid>https://velog.io/@h_rin/%ED%98%BC%EC%9E%90-%EA%B3%B5%EB%B6%80%ED%95%98%EB%8A%94-%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-%EB%94%A5%EB%9F%AC%EB%8B%9DChapter-05.-%ED%8A%B8%EB%A6%AC-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98</guid>
            <pubDate>Fri, 08 Nov 2024 16:34:48 GMT</pubDate>
            <description><![CDATA[<h1 id="05-1-결정-트리">05-1 결정 트리</h1>
<h2 id="키워드">[키워드]</h2>
<ul>
<li><code>결정 트리</code>: 예/아니오에 대한 질문을 이어나가면서 정답을 찾아 학습하는 알고리즘</li>
<li><code>불순도</code>: 결정트리가 최적의 질문을 찾기위한 기준, 사이킷런에서는 &#39;지니 불순도&#39;와 &#39;엔트로피 불순도&#39;제공</li>
<li><code>정보 이득</code>: 부모 노드와 자식 노드의 불순도 차이, 결정 트리의 알고리즘은 정보 이득이 최대화 되도록 학습</li>
<li><code>가지치기</code>: 결정 트리는 제한 없이 성장하면 훈련 세트에 과대적합되기 쉬움, 가지치기로 트리의 성장을 제한하는 방법
사이킷런의 결정 트리 알고리즘은 여러 가지치기 매개변수를 제공</li>
<li><code>특성 중요도</code>: 결정 트리에 사용된 특성이 불순도를 감소하는데 기여한 정도를 나타낸 값, 이를 계산할 수 있는 것이 결정 트리의 장점</li>
</ul>
<h2 id="로지스틱-회귀로-와인-분류하기">로지스틱 회귀로 와인 분류하기</h2>
<h4 id="데이터-준비">데이터 준비</h4>
<ul>
<li>psndas로 데이터 불러옴<pre><code>import pandas as pd
</code></pre></li>
</ul>
<p>wine = pd.read_csv(&#39;<a href="https://bit.ly/wine_csv_data&#39;">https://bit.ly/wine_csv_data&#39;</a>)</p>
<p>wine.head()</p>
<pre><code>
![](https://velog.velcdn.com/images/h_rin/post/c4af0b9d-7e9f-4c5b-8290-257bca01e86d/image.png)

- `.info()`: 데이터 타입, 누락 여부, 메모리 크기 등의 기본 정보를 보여주는 메소드
- `.describe()`: 각 열마다 간략한 통계치들을 출력해주는 메소드
</code></pre><p>wine.info()</p>
<p>wine.describe()</p>
<p>-&gt;
]
wine.info()
&lt;class &#39;pandas.core.frame.DataFrame&#39;&gt;
RangeIndex: 6497 entries, 0 to 6496
Data columns (total 4 columns):</p>
<h1 id="column---non-null-count--dtype">Column   Non-Null Count  Dtype</h1>
<hr>
<p> 0   alcohol  6497 non-null   float64
 1   sugar    6497 non-null   float64
 2   pH       6497 non-null   float64
 3   class    6497 non-null   float64
dtypes: float64(4)
memory usage: 203.2 KB</p>
<pre><code>       alcohol       sugar         pH           class</code></pre><p>count    6497.000000    6497.000000    6497.000000    6497.000000 
mean    10.491801    5.443235    3.218501    0.753886    # 평균
std        1.192712    4.757804    0.160787    0.430779 # 표준편차
min        8.000000    0.600000    2.720000    0.000000 # 최소
25%        9.500000    1.800000    3.110000    1.000000 # 1사분위수
50%       10.300000    3.000000    3.210000    1.000000 #중간값/2사분위수
75%       11.300000    8.100000    3.320000    1.000000 # 3사분위수
max       14.900000    65.800000    4.010000    1.000000 # 최대</p>
<pre><code>
- `test_size`: 테스트 데이터로 나눌 크기 ex) 0.2 -&gt; 20%</code></pre><p>data = wine[[&#39;alcohol&#39;, &#39;sugar&#39;, &#39;pH&#39;]].to_numpy()
target = wine[&#39;class&#39;].to_numpy()</p>
<p>from sklearn.model_selection import train_test_split</p>
<p>train_input, test_input, train_target, test_target = train_test_split(
    data, target, test_size=0.2, random_state=42)</p>
<p>print(train_input.shape, test_input.shape)</p>
<p>-&gt;
(5197, 3) (1300, 3)</p>
<h1 id="훈련세트-5197개-테스트-세트-1300개">훈련세트: 5197개, 테스트 세트: 1300개</h1>
<pre><code>
- StandardScaler 클래스 사용해 훈련 세트 전처리</code></pre><p>from sklearn.preprocessing import StandardScaler</p>
<p>ss = StandardScaler()
ss.fit(train_input)</p>
<p>train_scaled = ss.transform(train_input)
test_scaled = ss.transform(test_input)</p>
<pre><code>
- 표준점수로 변환된 train_scaled와 test_scaled를 사용해 로지스틱 회귀 모델 훈련
</code></pre><p>from sklearn.linear_model import LogisticRegression</p>
<p>lr = LogisticRegression()
lr.fit(train_scaled, train_target)</p>
<p>print(lr.score(train_scaled, train_target))
print(lr.score(test_scaled, test_target))</p>
<p>-&gt;
0.7808350971714451
0.7776923076923077</p>
<pre><code>=&gt; 훈련 세트와 테스트 세트의 점수가 모두 낮음, 과소적합
&gt; 문제 해결을 위해
규제 매개변수 C의 값 변경 or solver 매개변수에서 다른 알고리즘 선택 or 다항특성 추가


## 설명하기 쉬운 모델과 어려운 모델

- 로지스틱 회귀가 학습한 계수와 절편 출력</code></pre><p>print(lr.coef_, lr.intercept_)</p>
<p>-&gt; 
[[ 0.51268071  1.67335441 -0.68775646]] [1.81773456]</p>
<pre><code>
#### 로지스틱 회귀의 단점
- 로지스틱 회귀모델은 계수와 절편을 학습, but 그 숫자들이 정확히 어떤 의미인지 설명하기 쉽지 않음
ex) &quot;알코올 도수*0.512720274 + 당도*1.6733911 - pH*0.68767781 + 1.81777902를 계산한 값이 0보다 크면 화이트와인입니다..!&quot;

- 다항 특성을 추가한다면 더 복잡해짐
ex) (알코올도수∗pH) or (당도^2) 뭐라고 설명하지?

## 결정 트리(Decision Tree)
: 예/아니오에 대한 질문을 이어나가면서 정답을 찾는 알고리즘
: 비교적 예측 과정을 이해하기 쉽고 성능 Good

#### 결정 트리 모델 훈련
- `DecisionTreeClassifier()`: 결정트리 알고리즘
</code></pre><p>from sklearn.tree import DecisionTreeClassifier</p>
<p>dt = DecisionTreeClassifier(random_state=42)
dt.fit(train_scaled, train_target)</p>
<p>print(dt.score(train_scaled, train_target))
print(dt.score(test_scaled, test_target))</p>
<p>-&gt;
0.996921300750433
0.8592307692307692</p>
<pre><code>=&gt; 훈련 세트에 대한 점수 높음, 테스트 세트 상대적으로 낮음, 과대적합
</code></pre><p>import matplotlib.pyplot as plt
from sklearn.tree import plot_tree</p>
<p>plt.figure(figsize=(10,7))
plot_tree(dt)
plt.show()</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/538f85ee-8d7d-4c06-b488-5bbd9105e2a1/image.png)

- 트리의 깊이를 제한하여 다시 그려보기
- `max_depth`: 루트 노드 및에 몇 층을 더 그릴지 설정하는 매개변수
- `filled`: 클래스의 비율에 맞게 노드를 색칠해주는 매개변수
- `feature_names`: 특성의 이름을 전달하는 매개변수 (없으면 &#39;x[1]&#39;으로 나옴)
</code></pre><p>plt.figure(figsize=(10,7))
plot_tree(dt, max_depth=1, filled=True, feature_names=[&#39;alcohol&#39;, &#39;sugar&#39;, &#39;pH&#39;])
plt.show()</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/1f36ffdf-1ba7-4999-adfe-3a56216abc4d/image.png)

![](https://velog.velcdn.com/images/h_rin/post/661df792-135a-4597-a9ea-578f09772359/image.png)

#### 불순도
- 노드를 나누는 기준이 되는 수치
- `criterion`매개변수로 설정 가능(기본값: `gini`)

&gt; 지니 불순도 = 1 - (음성 클래스 비율^2 + 양성 클래스 비율^2)

- 불순도 0.5: 두 클래스가 정확히 반반으로 나누어져서 구분의 의미X, 최악의 노드
- 불순도 0: 노드에 한 클래스만 있는 경우, 완벽히 구분된 최선의 노드 = &#39;순수 노드&#39;

- 결정 트리 모델은 부모노드와 자식노드의 불순도 차이가 크도록 노드를 나눔
    -&gt; 이 차이를 &#39;정보 이득&#39;이라 함, 노드를 순수하게 나눌 수록 이득이 커짐

[정보 이득 계산(불순도 차이)]
&gt; 부모의 불순도 - (왼쪽 노드 샘플 수/부모노드 샘플 수) x 왼쪽 노드 불순도 - (오른쪽 노드 샘플 수/부모의 샘플 수) x 오른쪽 노드 불순도

*DecisionTreeClassifier에서 불순도 차이가 크도록 알아서 노드를 분할 시킴

#### 가지치기
- 무작정 깊이 훈련 시키면 훈련세트에만 과대적합됨
    *트리의 깊이 제한x시, 리프노드가 순수노드가 될 때까지 함
- 이를 막기 위해 &#39;가지 치기&#39;함 -&gt; `max_depth`로 깊이 지정
(참고) 결정 트리는 선형 회귀처럼 가중치x -&gt; L1, L2 규제 사용x
</code></pre><h1 id="앞에서-훈련을-과도하게-하여-과대적합이-되었어서-규제-필요">앞에서 훈련을 과도하게 하여 과대적합이 되었어서 규제 필요</h1>
<p>dt = DecisionTreeClassifier(max_depth=3, random_state=42)
dt.fit(train_scaled, train_target)</p>
<p>print(dt.score(train_scaled, train_target))
print(dt.score(test_scaled, test_target))</p>
<p>-&gt;
0.8454877814123533
0.8415384615384616</p>
<pre><code>=&gt; 깊이 3으로 지정하여 모델 만듦, 훈련 세트 성능 낮아졌지만 테스트 세트의 성능은 그대로
((과소적합된 거 아닌강..)) -&gt; 0.8넘으면 낮은 건 아닌듯!
</code></pre><p>plt.figure(figsize=(20,15))
plot_tree(dt, filled=True, feature_names=[&#39;alcohol&#39;, &#39;sugar&#39;, &#39;pH&#39;])
plt.show()</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/c5f36726-a602-44ee-87a5-5997b68ef254/image.png)

↪ 왼쪽에서 3번째 노드에 도착하는 경우만 음성 클래스(red)로 예측!
↪ &quot;−0.802&lt;sugar≤−0.239 이면서 alcohol≤0.454 인 경우만 red로 분류되고, 나머지 경우는 white로 분류하는 모델이 완성

#### 결정트리의 장점
- 표준화 전처리 필요 X
    : 위의 결과에서 당도가 -0.802 음수로 설명되어 있음. 그런데 불순도의 정의를 보면 클래스별 비율을 가지고 계산하기 때문에 특성값의 스케일이 영향을 미치지 않음
</code></pre><p>dt = DecisionTreeClassifier(max_depth=3, random_state=42)
dt.fit(train_input, train_target) #표준화 하기 전 input으로 넣음</p>
<p>print(dt.score(train_input, train_target))
print(dt.score(test_input, test_target))</p>
<p>-&gt;
0.8454877814123533
0.8415384615384616</p>
<pre><code>=&gt; 전처리 안 한 `train_input`으로 넣어도 값이 같은 걸 알 수 있음
↪ &quot;1.625&lt;sugar≤4.325 이면서 alcohol≤11.025 인 경우만 red로 예측
</code></pre><p>plt.figure(figsize=(20,15))
plot_tree(dt, filled=True, feature_names=[&#39;alcohol&#39;, &#39;sugar&#39;, &#39;pH&#39;])
plt.show() # 그려봤을 때 숫자들 양수로 나옴 -&gt; 해석 쉬워짐</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/2e010adb-a83a-4fc7-8e60-fd48f4935706/image.png)

- `dt.feature_importances_`: 어떤 특성이 가장 유용한지 &#39;특성 중요도&#39; 계산해줌</code></pre><p>print(dt.feature_importances_)</p>
<p>-&gt;
[0.12345626 0.86862934 0.0079144 ]</p>
<h1 id="alcohol-----sugar--------ph">alcohol     sugar        pH</h1>
<pre><code>=&gt; sugar, 당도가 가장 중요도가 높은 것을 알 수 있음

- 그래프를 직관적으로 이해하기 좋음
  : 특정 클래스 비율이 높아질 수록 색깔이 진해지기 때문에 어떤 노드가 어떤 클래스인지 한 눈에 보기 쉬움
ex) 위 그래프에서 3번째 노드만 주황색으로 칠해져있어 이 노드만 음성 클래스이고 나머지는 양성 클래스라는 것을 알 수 있음

- 회귀모델보다 설명하기 쉬움
  : 어떤 조건이면 어떤 클래스인지가 명확하게 구분되기에 해석이 쉬움
  다만, 트리의 깊이가 깊어지면 해석이 쉽지 않아질 수 있음


# 05-2 교차 검증과 그리드 서치
## [키워드]
#### 검증세트
: 하이퍼파라미터 튜닝을 위한 모델을 평가할 때, 테스트 세트를 사용하지 않기 위해 훈련 세트에서 다시 떼어낸 데이터 세트

#### 교차 검증
: 훈련 세트를 여러 폴드로 나눈 다음 한 폴드가 검증 세트의 역할을 하고 나머지 폴드에서는 모델 훈련을 함
-&gt; 이런 식으로 모든 폴드에 대해 검증 점수를 얻어 평균을 내는 방법

#### 그리드 서치
: 하이퍼파라미터 탐색을 자동화해 주는 도구, 탐색할 매개변수를 나열하면 교차 검증을 수행하여 가장 좋은 검증 점수의 매개변수 조합을 선택 -&gt; 이 매개변수의 조합으로 최종 모델을 훈련함

#### 랜덤 서치
: 연속된 매개변수 값을 탐색할 때 유용, 탐색할 값을 직접 나열하는 것이 아니라 탐색 값을 샘플링할 수 있는 확률 분포 객체를 전달
- 지정된 횟수만큼 샘플링하여 교차 검증을 수행하기 때문에 시스템 자원이 허락하는 만큼 탐색량 조절 가능

## 검증세트
- 훈련/검증/테스트 3개의 세트로 나눔
![](https://velog.velcdn.com/images/h_rin/post/7d1c8328-5289-4d4c-b1e9-0e2676c90c1e/image.png)

+) 테스트 세트와 검증 세트를 얼마나 나눠야 될까?
: 보통 20~30% 테스트/검증으로 나누지만 훈련 데이터가 많다면 단 몇 %만 떼어 놓아도 전체 데이터를 대표하는데 문제 X

- **일반적인 활용 과정**
1) 모델을 훈련세트로 훈련(fit)하고, 검증세트로 평가(score)한다
2) 매개변수 바꿔가며 score 점수 가장 좋은 모델을 고른다
3) 최적의 매개변수로, 훈련세트+검증세트 합친 걸 다시 훈련(fit)한다
4) 그리고 맨 마지막에 테스트세트로 최종 점수(score)를 평가한다

#### 검증 세트를 활용한 모델 평가</code></pre><p>import pandas as pd # 판다스 데이터 불러오기
wine = pd.read_csv(&#39;<a href="https://bit.ly/wine_csv_data&#39;">https://bit.ly/wine_csv_data&#39;</a>)</p>
<h1 id="전과-동일하게-입력타깃-데이터-설정">전과 동일하게 입력/타깃 데이터 설정</h1>
<p>data = wine[[&#39;alcohol&#39;, &#39;sugar&#39;, &#39;pH&#39;]].to_numpy()
target = wine[&#39;class&#39;].to_numpy()</p>
<h1 id="훈련테스트-세트-분할">훈련/테스트 세트 분할</h1>
<p>from sklearn.model_selection import train_test_split</p>
<p>train_input, test_input, train_target, test_target = train_test_split(
    data, target, test_size=0.2, random_state=42)</p>
<h1 id="한-번-더-훈련검증val_-세트-분할">한 번 더 훈련/검증(val_) 세트 분할</h1>
<p>sub_input, val_input, sub_target, val_target = train_test_split(
    train_input, train_target, test_size=0.2, random_state=42)</p>
<p>print(sub_input.shape, val_input.shape)</p>
<p>-&gt;
(4157, 3) (1040, 3)</p>
<pre><code>=&gt; 크기 출력을 확인해보면 5197개였던 훈련세트가 한 번 더 쪼개진 것을 확인 가능

- 이후 모델 평가 시 `test`말고 `val`로 평가</code></pre><p>from sklearn.tree import DecisionTreeClassifier</p>
<h1 id="결정-트리-모델-만듦">결정 트리 모델 만듦</h1>
<p>dt = DecisionTreeClassifier(random_state=42)
dt.fit(sub_input, sub_target) # 훈련</p>
<p>print(dt.score(sub_input, sub_target)) # 훈련 세트 평가
print(dt.score(val_input, val_target)) # 검증 세트 평가</p>
<p>-&gt;
0.9971133028626413
0.864423076923077</p>
<pre><code>
#### 검증세트의 문제점
- 보통 많은 데이터 훈련해야 좋은 모델이 됨 하지만 테스트/검증 세트로 떼어내면 훈련할 데이터가 줄어 들음
- 그렇다고 검증 세트를 줄이면 작은 표본으로 평가하기에 신뢰성 떨어짐

-&gt; 이를 해결하기 위해 나온 것이 &#39;교차 검증&#39;

## 교차 검증
- 훈련 세트에서 검증 세트를 이분할 하는 것이 아니라 훈련 세트를 조각내놓고 그 조각들을 번갈아가면서 검증세트가 되도록 하는 방식
- 여기서 &#39;조각&#39;=&#39;폴드&#39;, 보통 5-폴드 or 10-폴드 교차검증을 많이 사용
- 결론적으로 데이터의 8-90%까지 훈련가능하고 검증 점수고 모든 폴드 경우의 평균으로 구하기 때문에 신뢰성이 떨어지지 않음

![](https://velog.velcdn.com/images/h_rin/post/6294d65b-c4bd-43f1-bd9c-30941eb38eb6/image.png)

+) k-폴드 교차 검증이라고 함 / 훈련 세트를 몇부분으로 나누야에 따라 다르게 부름

#### 교차 검증을 활용한 모델 평가
- `cross_validate()` : 사이킷런에서 제공하는 교차검증 함수
    -&gt; 직접 검증세트를 떼어낼 필요X, 1차로 떼어낸 훈련 세트를 전달하면 알아서 검증 세트 나누고 폴드마다 바꿔가면서 평가해줌
- `cv`: 이 매개변수로 폴드 수를 지정 가능(기본값 = 5)
</code></pre><p>from sklearn.model_selection import cross_validate</p>
<p>scores = cross_validate(dt, train_input, train_target)
print(scores)</p>
<p>-&gt;
{&#39;fit_time&#39;: array([0.01341891, 0.02167416, 0.02525187, 0.04882073, 0.03598666]), &#39;score_time&#39;: array([0.0027864 , 0.0019815 , 0.00886154, 0.01437068, 0.02624893]), &#39;test_score&#39;: array([0.86923077, 0.84615385, 0.87680462, 0.84889317, 0.83541867])}</p>
<pre><code>- 각 폴드별 검증 점구는 &#39;test_score&#39;에 저장됨, 이 점수들을 평균하면 교차 검증의 최종 점수가 됨
(test라고 적혀 있지만 테스트 세트의 점수가 아님)
</code></pre><p>import numpy as np</p>
<p>print(np.mean(scores[&#39;test_score&#39;]))</p>
<p>-&gt;
0.855300214703487</p>
<pre><code>+) 교차 검증은 train_test_split처럼 알아서 훈련세트를 섞어주지는 않음. 훈련세트를 섞으려면 따로 &#39;분할기&#39;를 지정해줘야 함. 

## 하이퍼파라미터 튜닝
- 모델이 학습하는 변수 = &#39;모델 파라미터&#39;
- 사용자가 지정해주는 변수 = &#39;하이퍼파라미터(=매개변수)&#39;
- 매개변수를 바꿔가면서 score의 점수를 보고 최적의 매개변수를 찾음(검증세트로 해야함)

&gt; ✌🏻그런데 여기서 짚고 넘어가야 할 포인트가 두 가지 있다.
Point① 그럼 우리가 매개변수 바꿔가면서 일일이 cross_validate 하고 있어야 하나?
Point② 사실, 매개변수들의 최적값은 순차적으로 찾을 수 없게 되어있다. 이를테면, max_depth 최적값 찾아서 고정해놓고 그 다음으로 min_samples_split을 바꿔가면서 찾는 것이 불가능하다. 매개변수 상호 간에 영향을 끼치기 때문이다.
그렇다면,
동시에 매개변수를 바꿔가며 최적의 값을 찾는 이 복잡한 과정을 어찌할 것인가?

#### 그리드 서치
- `GridSearchCV()`: 사이킷런에서 제공하는 하이퍼파라미터 탐색(최적의 매개변수 탐색)과 교차 검증을 한 번에 수행해주는 클래스
- `parms`: 딕셔너리 형태로 탐색할 매개변수 후보들을 전달하기 위해 사용한 변수
- `n_jobs`: 작업에 사용할 cpu 코어 수를 지정하는 매개변수(기본값 = 1)
    -&gt; -1로 지정 시 모든 코어 사용(느려짐)
</code></pre><p>from sklearn.model_selection import GridSearchCV</p>
<h1 id="cross_valdate-필요x">cross_valdate 필요X</h1>
<p>params = {&#39;min_impurity_decrease&#39;: [0.0001, 0.0002, 0.0003, 0.0004, 0.0005]}</p>
<h1 id="매개변수-이름은-문자열로-입력해야-함">매개변수 이름은 문자열로 입력해야 함</h1>
<p>gs = GridSearchCV(DecisionTreeClassifier(random_state=42), params, n_jobs=-1) # 그리드 서치 객체 생성</p>
<p>gs.fit(train_input, train_target)</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/554abf47-7ecf-447f-b652-dd1232effa3e/image.png)

- `.best_params_` : 찾아낸 최적의 매개변수가 저장된 곳
- `.best_estimator_` : 최적의 매개변수 조합으로 훈련시킨 최적의 모델이 저장된 곳
</code></pre><p>dt = gs.best_estimator_
print(dt.score(train_input, train_target))</p>
<p>-&gt;
0.9615162593804117</p>
<pre><code></code></pre><p>print(gs.best_params_)</p>
<p>-&gt;
{&#39;min_impurity_decrease&#39;: 0.0001}</p>
<pre><code>
- `.cv_results_` 딕셔너리의 &#39;mean_test_score&#39;키에 교차검증의 평균 점수가 저장됨
↪ 그 중에 제일 큰 값의 인덱스를 .argmax()로 꺼내고, &#39;params&#39; 키를 거쳐서, 최적의 매개변수를 뽑아볼 수도 있음. 
</code></pre><p>print(gs.cv_results_[&#39;mean_test_score&#39;])</p>
<p>-&gt;
[0.86819297 0.86453617 0.86492226 0.86780891 0.86761605]</p>
<pre><code></code></pre><p>best_index = np.argmax(gs.cv_results_[&#39;mean_test_score&#39;])
print(gs.cv_results_[&#39;params&#39;][best_index]) #parms 중 best_index 번째에 있는 값</p>
<p>-&gt;
{&#39;min_impurity_decrease&#39;: 0.0001}</p>
<pre><code>[과정 정리]
&gt; 1. 탐색할 매개변수 지정
2. 그다음 훈련 세트에서 그리드 서치를 수행하여 최상의 평균 검증 점수가 나오는 매개변수 조합을 찾음 -&gt; 이 조합은 그리드 객체에 저장
3. 그리드 서치는 최상의 매개변수에서 (교차 검증에 사용한 훈련 세트가 아니라) 전체 훈련 세트를 사용해 최종 모델을 훈련함 -&gt; 이 모델도 그리드 서치 객체에 저장

#### 좀 더 복잡한 매개변수 조합
- 넘파이의 `np.arange()`함수와 파이썬의 `range()`함수를 활용</code></pre><h1 id="arange와-range로-여러-개-한-번에-지정해-놓고-교차-검증">arange와 range로 여러 개 한 번에 지정해 놓고 교차 검증</h1>
<p>params = {&#39;min_impurity_decrease&#39;: np.arange(0.0001, 0.001, 0.0001),
          &#39;max_depth&#39;: range(5, 20, 1),
          &#39;min_samples_split&#39;: range(2, 100, 10)
          }</p>
<p>gs = GridSearchCV(DecisionTreeClassifier(random_state=42), params, n_jobs=-1) # 객체 생성
gs.fit(train_input, train_target) # 훈련</p>
<pre><code>
- 그리드 서치가 최적의 매개변수 조합과 검증 점수를 구함
</code></pre><p>print(gs.best_params_) # 최상의 매개변수들 확인 가능</p>
<h1 id="개별이-아니라-이-조합으로-사용해야-함">개별이 아니라 이 조합으로 사용해야 함</h1>
<p>-&gt;
{&#39;max_depth&#39;: 14, &#39;min_impurity_decrease&#39;: 0.0004, &#39;min_samples_split&#39;: 12}</p>
<pre><code></code></pre><p>print(np.max(gs.cv_results_[&#39;mean_test_score&#39;]))</p>
<h1 id="최상의-조합을-썼을-때-검증-점수-확인">최상의 조합을 썼을 때 검증 점수 확인</h1>
<p>-&gt;
0.8683865773302731</p>
<pre><code>
&gt;✌🏻그런데, 여기서 또 한 번 아쉬운 점이 두 가지 있다.
Point① 위에서 매개변수 후보 간격들을 0.001, 1, 10으로 했었는데, 굳이 이렇게 간격을 둔 것에 대한 특별한 근거가 없다.
Point② 또, 너무 많은 매개변수 조합이 있어서 그리드서치 수행 시간이 오래 걸릴 수 있다. (위 예시는 무려 9*15*10*5=6,750 가지나 있었다)


#### 랜덤 서치
- `RandomizedSearchCV()`: 매개변수의 범위나 간격을 미리 정하기 어려우니, 그냥 넓은 범위를 던져주고 &quot;이 안에서 랜덤하게 매개변수들 뽑아서 해보고, 최적인 걸 찾아줘!&quot;

+) 사이파이 라이브러리의 uniform과 randint 클래스를 활용하면 됨
</code></pre><p>from scipy.stats import uniform, randint</p>
<h1 id="그리드-서치랑-똑같지만-range대신-난수발생기를-사용randint">그리드 서치랑 똑같지만 range대신 난수발생기를 사용(randint)</h1>
<p>params = {&#39;min_impurity_decrease&#39;: uniform(0.0001, 0.001),
          &#39;max_depth&#39;: randint(20, 50),
          &#39;min_samples_split&#39;: randint(2, 25),
          &#39;min_samples_leaf&#39;: randint(1, 25),
          }</p>
<pre><code>
- 대부분의 구성은 그리드 서치와 동일 하지만 `n_iter`로 샘플링 횟수 지정해줘야 함</code></pre><h1 id="랜덤서치-객체-생성-및-훈련">랜덤서치 객체 생성 및 훈련</h1>
<p>from sklearn.model_selection import RandomizedSearchCV</p>
<p>gs = RandomizedSearchCV(DecisionTreeClassifier(random_state=42), params,
                        n_iter=100, n_jobs=-1, random_state=42)
                        # 매개변수 후보 몇개 뽑을 건지 지정!
gs.fit(train_input, train_target)</p>
<pre><code>
- 그리드 서치보다 더 넓은 영역을 무작위로 탐색하면서 교차검증 수를 줄임
(최적의 매개변수 조합과 검증 점수도 그리드 서치와 동일하게 계산)</code></pre><p>print(gs.best_params_) # 최적의 매개변수 저장</p>
<p>-&gt;
{&#39;max_depth&#39;: 39, &#39;min_impurity_decrease&#39;: 0.00034102546602601173, &#39;min_samples_leaf&#39;: 7, &#39;min_samples_split&#39;: 13}</p>
<pre><code></code></pre><p>print(np.max(gs.cv_results_[&#39;mean_test_score&#39;])) #최적의 매개변수들로 구한 검증세트 점수</p>
<p>-&gt;
0.8695428296438884</p>
<pre><code>
- 검증세트로 필요한 검즌 다 하고 나면, 마지막에 한 번만 테스트 세트 확인</code></pre><p>dt = gs.best_estimator_</p>
<p>print(dt.score(test_input, test_target))</p>
<p>-&gt;
0.86</p>
<pre><code>
&gt;와인 구분하는 결정트리 모델 만들었지만, &#39;테스트 세트에만 맞춰진 거 아니냐&#39;는 이사님의 일침 → 검증 세트 추가 → but 검증세트까지 떼어내는 부담 있음 → 교차검증으로 극복! → 근데 매개변수를 바꿔가면서 평가하는 과정이 지루하고 복잡해짐ㅠ → 그 과정을 자동화한 그리드서치 활용! → but 간격 설정에 관한 한계가 있고, 시간이 오래 걸림 → 넓은 범위 속을 무작위로 탐색하여 효율적인 랜덤서치로 극복!

# 05-3 트리의 앙상블
## [키워드]
#### 앙상블 학습
: 더 좋은 예측 결과를 만들기 위해 여러 개의 모델을 훈련하는 머신러닝 알고리즘

#### 랜덤 포레스트
: 대표적인 결정 트리 기반의 앙상블 학습법
- 부트스트랩 샘플을 사용하고 일부 특성을 선택하여 트리를 만드는 것이 특징

#### 엑스트라 트리
: 랜덤 포레스트와 비슷하게 결정 트리를 사용하여 앙상블 모델을 만들지만 부트스트랩 샘플을 사용하지 않음
- 랜덤하게 노드를 분할해 과대적합을 감소시킴

#### 그레디언트 부스팅
: 랜덤 포레스트나 엑스트라 트리와 달리 결정 트리를 연속적으로 추가하여 손실함수를 최소화 하는 앙상블 방법
- 훈련 속도가 조금 느리지만 더 좋은 성능을 기대할 수 있음
- 그래디언트 부스팅 속도를 개선한 것 -&gt; 히스토그램 기반 그레디언트 부스팅 =&gt; 안정적인 결과와 높은 성능으로 인기 좋음


## 정형 데이터와 비정형 데이터
- 정형 데이터: csv나 database 혹은 excel처럼 어떤 구조로 가지런히 정리되어있는 데이터, 주로 머신러닝 알고리즘을 통해 다룸
- 비정형 데이터: 텍스트, 사진, 음악 등 database나 excel로 표현하기 어려운 데이터, 주로 딥러닝(신경망) 알고리즘을 통해 다룸

### 앙상블 학습
- 정형 데이터를 다루는 데 가장 뛰어난 성과를 내는 알고리즘으로, 여러 개의 모델을 합쳐서 더 좋은 결과를 도출함
- 주로 결정 트리를 기반으로 만들어져 있음

## 랜덤 포레스트
- 앙상블 학습의 대표, 랜덤한 결정트리의 &#39;숲&#39;이라고 봄

&gt; **랜덤포레스트의 기본 원리**
: Ⓐ+Ⓑ로 개별 트리에 무작위성을 부여해서 트리의 성능이 너무 강력해지는 것을 막음 (=과대적합 방지) → 물론 개별트리의 성능은 떨어짐 but 그걸 여러 개 묶어서 일반화하면 높은 성능이 나오게 됨!

### Ⓐ 훈련세트에 무작위성 주입
- 랜덤포레스트 속의 각 트리는 우리가 입력한 훈련데이터를 그대로 학습하지 않고, 훈련세트와 같은 크기의 부트스트랩 샘플(=중복을 허용한 추출)을 만들어 학습

![](https://velog.velcdn.com/images/h_rin/post/2e326cac-ed44-43f6-bfd6-fa59ae6418d0/image.png)

![](https://velog.velcdn.com/images/h_rin/post/0ffe59cf-6f7b-48cf-8668-6aecc578ae72/image.png)

### Ⓑ 특성 선택에 무작위성 주입
- 노드를 분할할 때도(트리를 성장시킬 때도) 모든 특성을 다 써서 최선의 분할을 하는 게 아니라,√특성개수 개의 특성만 써서(일부러 특성 개수 줄여서) 분할

![](https://velog.velcdn.com/images/h_rin/post/be373d59-8f35-4c4a-8017-c68cc3dac53b/image.png)

➡️ Ⓐ+Ⓑ의 방식으로 100개(기본값)의 결정트리를 훈련하고, (분류문제인 경우) 각 트리의 클래스별 확률을 평균하여 가장 높은 확률의 클래스를 예측클래스로!
cf. (회귀문제인 경우) &#39;확률&#39; 대신 &#39;예측값(임의의 수치)&#39;을 평균하면 됨!

### 실습
#### 데이터 준비</code></pre><p>import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split</p>
<h1 id="판다스로-불러오기">판다스로 불러오기</h1>
<p>wine = pd.read_csv(&#39;<a href="https://bit.ly/wine_csv_data&#39;">https://bit.ly/wine_csv_data&#39;</a>)</p>
<p>#입력/타깃 설정
data = wine[[&#39;alcohol&#39;, &#39;sugar&#39;, &#39;pH&#39;]].to_numpy()
target = wine[&#39;class&#39;].to_numpy()</p>
<p>#훈련/테스트 세트 분할
train_input, test_input, train_target, test_target = train_test_split(data, target, test_size=0.2, random_state=42)</p>
<pre><code>
#### 모델 훈련과 교차 검증
- `RandomForestClassifier()` : 사이킷런의 ensemble 패키지에 있음
- `return_train_score` : 훈련세트의 점수도 반환하도록 하는 설정 (for 과대적합 파악)
</code></pre><p>from sklearn.model_selection import cross_validate # 교차 검증
from sklearn.ensemble import RandomForestClassifier</p>
<p>rf = RandomForestClassifier(n_jobs=-1, random_state=42)
scores = cross_validate(rf, train_input, train_target, return_train_score=True, n_jobs=-1)</p>
<h1 id="훈련세트-점수--검증세트-점수-출력">훈련세트 점수 / 검증세트 점수 출력</h1>
<p>print(np.mean(scores[&#39;train_score&#39;]), np.mean(scores[&#39;test_score&#39;]))</p>
<p>-&gt;
0.9973541965122431 0.8905151032797809</p>
<pre><code>=&gt; 결정 트리 1개 썼을 때 보다 더 나은 점수가 나옴
(과대적합 약간 있지만, 일단 이 상태가 만족스럽다고 가정하고 그냥 최종모델이라 치고 다음 단계로)

#### 특성 중요도 확인
- 각 결정트리의 `feature_importances_` 취합 → 랜덤포레스트의 `feature_importances_` 
➡️ 특성 선택에 무작위성을 넣었기 때문에, 단일 결정트리보다 골고루 나옴
</code></pre><p>rf.fit(train_input, train_target)
print(rf.feature_importances_) # 특성 중요도 출력</p>
<p>-&gt;
[0.23167441 0.50039841 0.26792718]</p>
<pre><code>
## 엑스트라 트리
- 개별 트리 성능을 억제하지만 많은 트리를 앙상블해서 과대적합 문제와 점수를 동시에 챙기는 기본적인 논리는 랜덤포레스트와 매우 비슷하지만 Ⓐ 대신 Ⓒ를 활용

#### Ⓑ 특성 선택에 무작위성 주입
- 랜덤 포레스트와 동일

#### Ⓒ 노드 분할에 무작위성 주입
- 랜덤 포레스트가 &#39;부트스트랩 샘플(Ⓐ)&#39;을 사용했던 것과 달리, 엑스트라 트리는 우리가 입력한 훈련세트 전체를 그대로 사용
- 대신, 노드를 분할할 때 최선의 분할(불순도 차이가 가장 큰 분할)을 찾는 게 아니라 그냥 무작위로 분할(랜덤하게 분할한 후보들 중에 그나마 불순도 차이 큰 걸로)
- 269p, 262p 참고!

&gt; ❗ 분할 자체를 무작위로 하다보니 랜덤 포레스트보다 무작위성이 더 크다. 그래서 랜덤포레스트보다는 트리 개수를 더 많이 해야 좋은 성능을 낸다고 알려져있다. 하지만
더 큰 무작위성 때문에 계산속도는 상대적으로 더 빨라진다는 장점도 있다.

### 실습
#### 데이터 준비
- 앞과 동일

#### 모델 훈련과 교차 검증
- `ExtraTreesClassifier()` : 역시 ensemble 패키지에 있음
</code></pre><p>from sklearn.ensemble import ExtraTreesClassifier</p>
<p>#엑스트라 트리로 교차검증
et = ExtraTreesClassifier(n_jobs=-1, random_state=42)
scores = cross_validate(et, train_input, train_target, return_train_score=True, n_jobs=-1)</p>
<p>#훈련 세트 점수 / 검증 세트 점수
print(np.mean(scores[&#39;train_score&#39;]), np.mean(scores[&#39;test_score&#39;]))</p>
<p>-&gt;
0.9974503966084433 0.8887848893166506</p>
<pre><code>
#### 특성 중요도 확인
- 마찬가지로 `feature_importances_` 제공하고, 마찬가지로 비교적 골고루 나옴
(= 단일 결정트리보다 &#39;sugar(당도)&#39; 특성에 대한 의존성 ↓)
</code></pre><p>et.fit(train_input, train_target)
print(et.feature_importances_)</p>
<p>-&gt;
[0.20183568 0.52242907 0.27573525]</p>
<pre><code>
## 그레디언트 부스팅(GB)
- 깊이가 얕은 결정 트리를 사용하여 이전 트리의 오차를 보완하는 방식의 앙상블 학습
(기본값=3 / 깊이가 얕으니까 역시 과대적합을 방지할 수 있음.)
- &#39;Gradient&#39;에서 유추할 수 있듯, 경사 하강법을 사용해 트리를 추가하는 거임!
(분류 : 로지스틱 손실함수✔️ / 회귀 : 평균제곱오차 함수)

#### 이전 트리의 손실을 보완하는 방향으로 트리 추가
❗ 경사하강법에서 손실함수의 낮은 곳을 찾아 조금씩 이동했던 것처럼❗

- 깊이가 얕은 트리를 사용해서 트리의 성능이 강력해지는 것을 막음 (=과대적합 방지)
→ 물론 처음에 score 구해보면 성능이 많이 높지 않음 
→but 그건 트리 개수 점점 더 넣으면서 높이면 됨! (결정트리 개수 늘려도 과대적합에 강하다는 장점)
- 조금씩 이동하도록 학습률도 조절함 by learning_rate (기본값=0.1)

![](https://velog.velcdn.com/images/h_rin/post/61ad903b-8e91-4726-b875-2db3a3ff76d2/image.png)

### 실습
#### 데이터 준비
- 앞과 동일

#### 모델 훈련 &amp; 교차검증
- `GradientBoostClassifier()` : ensemble 패키지에 있음
    -&gt; 과대적합이 거의 없음, 성능이 좀 낮음 -&gt; 트리 더 넣어서 높이면 됨
</code></pre><p>from sklearn.ensemble import GradientBoostingClassifier</p>
<p>#그레디언트 부스팅으로 교차 검증
gb = GradientBoostingClassifier(random_state=42)
scores = cross_validate(gb, train_input, train_target, return_train_score=True, n_jobs=-1)</p>
<p>#훈련세트 점수 / 검증세트 점수
print(np.mean(scores[&#39;train_score&#39;]), np.mean(scores[&#39;test_score&#39;]))</p>
<p>-&gt;
0.8881086892152563 0.8720430147331015</p>
<pre><code>- `n_estimators` : 추가할 트리 개수 설정 
- `learning_rate` : 학습률 설정
</code></pre><h1 id="트리-개수-늘리고-학습률도-약간-높임">트리 개수 늘리고 학습률도 약간 높임</h1>
<p>gb = GradientBoostingClassifier(n_estimators=500, learning_rate=0.2, random_state=42)
scores = cross_validate(gb, train_input, train_target, return_train_score=True, n_jobs=-1)</p>
<h1 id="트리-개수를-5배-늘림---검증-세트-점수는-크게-변화-x-과대적합-억제">트리 개수를 5배 늘림 -&gt; 검증 세트 점수는 크게 변화 x (과대적합 억제)</h1>
<p>print(np.mean(scores[&#39;train_score&#39;]), np.mean(scores[&#39;test_score&#39;]))</p>
<p>-&gt;
0.9464595437171814 0.8780082549788999</p>
<pre><code>
#### 특성 중요도 확인
- 랜덤 포레스트에 비해 덜 골고루 나옴(일부 특성에 더 집중함)</code></pre><p>gb.fit(train_input, train_target)
print(gb.feature_importances_)</p>
<p>-&gt;
[0.15887763 0.6799705  0.16115187]</p>
<pre><code>
보통 GB가 랜덤 포레스트(RF)보다 좀 더 높은 성능을 내지만, 트리를 하나하나 추가하기 때문에 훈련속도가 느리다는 단점 존재

-&gt; 이걸 개선한 것이 &#39;히스토그램 기반 GB&#39;

## 히스토그램 기반 GB
- 정형 데이터를 다루는 머신러닝 알고리즘 중 가장 인기가 높음
- 기본 매개변수에서도 안정적인 성능을 얻을 수 있을 정도로 괜찮은 모델
- 노드를 분할하기 전에, 훈련데이터를 256개의 구간으로 쪼갬 
    → 특성의 범위가 짧게 끊어져있으니 최적의 분할을 매우 빠르게 찾을 수 있음
    ![](https://velog.velcdn.com/images/h_rin/post/1ed8c004-23b2-46d8-a52d-05ab0f91815b/image.png)

- 또한, 256(255+1)개 중에서 1개는 누락된 데이터를 위한 구간으로 할당함 → 훈련 데이터에 누락된 특성이 있더라도 이를 222p처럼 따로 전처리할 필요 없음

### 실습
#### 데이터 준비
- 앞에와 동일

#### 모델 훈련 &amp; 교차검증
- `HistGradientBoostClassifier()` : ensemble 패키지에 있음
- 추가 트리 개수 설정 시 `max_iter` 사용
</code></pre><p>from sklearn.ensemble import HistGradientBoostingClassifier</p>
<h1 id="히스토그램-기반-gb로-교차-검증">히스토그램 기반 GB로 교차 검증</h1>
<p>hgb = HistGradientBoostingClassifier(random_state=42) # 기본 설정으로 해도 충분히 good
scores = cross_validate(hgb, train_input, train_target, return_train_score=True, n_jobs=-1)</p>
<h1 id="훈련세트-점수--검증세트-점수">훈련세트 점수 / 검증세트 점수</h1>
<p>print(np.mean(scores[&#39;train_score&#39;]), np.mean(scores[&#39;test_score&#39;]))</p>
<p>-&gt;
0.9321723946453317 0.8801241948619236</p>
<pre><code>=&gt; 과대적합도 잘 억제, (기본 설정인데도) 그냥 GB보다 더 높은 성능

#### 특성 중요도 확인
- `permutation_importance()` 함수를 사용

- 특성을 하나씩 랜덤하게 섞으면서 모델의 성능 변화를 관찰 (n_repeats로 섞을 횟수 지정 가능) → 많이 변할수록 중요한 특성으로, 별로 안 변할수록 중요하지 않은 특성으로 계산
- 반환된 객체에는 importances (계산한 개별 특성중요도 모두), importances_mean(그걸 평균 낸 최종 특성중요도), importances_std(특성중요도들 간 표준편차) 가 담겨 있음
</code></pre><p>from sklearn.inspection import permutation_importance</p>
<h1 id="hgb-모델-훈련-먼저">HGB 모델 훈련 먼저</h1>
<p>hgb.fit(train_input, train_target)
result = permutation_importance(hgb, train_input, train_target, n_repeats=10,
                                random_state=42, n_jobs=-1)
print(result.importances_mean) # 특성 중요도 계산</p>
<p>-&gt;
[0.08876275 0.23438522 0.08027708]</p>
<pre><code>
- 이 함수는 훈련 세트뿐만 아니라 테스트 세트에서도 특성 중요도 계산 가능</code></pre><p>result = permutation_importance(hgb, test_input, test_target, n_repeats=10,
                                random_state=42, n_jobs=-1)
print(result.importances_mean)</p>
<p>-&gt;
[0.05969231 0.20238462 0.049]</p>
<pre><code>&#39;pH&#39;는 실전에서 의미없는 특성일 것으로 예상

#### 최종 성능 확인
- 이 모델을 최종모델로 하여, 테스트세트에서의 성능을 최종 확인해보면
단일 결정트리(86%였음)보다 좋은 결과를 얻을 수 있음</code></pre><p>hgb.score(test_input, test_target)</p>
<p>-&gt;
0.8723076923076923</p>
<pre><code>
## 그 외 라이브러리
- 많은 라이브러리에서 그레이디언트 부스팅 알고리즘을 구현하고 있음

#### XGBoost
- 다양한 부스팅 알고리즘을 지원하는 라이브러리 (오픈소스/코랩o)
- Kaggle에서 많이 사용하면서 유명해짐
</code></pre><p>from xgboost import XGBClassifier
                        #이렇게 설정하면 &#39;히스토그램 기반&#39; 
xgb = XGBClassifier(tree_method=&#39;hist&#39;, random_state=42)
scores = cross_validate(xgb, train_input, train_target, return_train_score=True, n_jobs=-1)
        # 사이킷런의 cross_validate도 함께 사용 가능</p>
<p>print(np.mean(scores[&#39;train_score&#39;]), np.mean(scores[&#39;test_score&#39;]))</p>
<p>-&gt;
0.9558403027491312 0.8782000074035686</p>
<pre><code>
#### LightGBM
- 마이크로소프트에서 만든, 그레디언트 부스팅 전용 라이브러리 (오픈소스/코랩o)
- 히스토그램 기반 GB를 지원해서 인기가 높아짐</code></pre><p>from lightgbm import LGBMClassifier</p>
<h1 id="lightgbm을-임포트">LightGBM을 임포트</h1>
<p>lgb = LGBMClassifier(random_state=42)
scores = cross_validate(lgb, train_input, train_target, return_train_score=True, n_jobs=-1)</p>
<h1 id="교차검증---사이킷런-부스팅-알고리즘의-결과와-거의-비슷">교차검증 -&gt; 사이킷런 부스팅 알고리즘의 결과와 거의 비슷</h1>
<p>print(np.mean(scores[&#39;train_score&#39;]), np.mean(scores[&#39;test_score&#39;]))</p>
<p>-&gt;
0.935828414851749 0.8801251203079884</p>
<p>```</p>
<p><a href="https://velog.io/@simon919/%ED%98%BC%EA%B3%B5%EB%A8%B8%EC%8B%A0-5-3.-%ED%8A%B8%EB%A6%AC%EC%9D%98-%EC%95%99%EC%83%81%EB%B8%94-wdvkdf13">참고/출처</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[혼자 공부하는 머신러닝 딥러닝]Chapter04. 다양한 분류 알고리즘]]></title>
            <link>https://velog.io/@h_rin/%ED%98%BC%EC%9E%90-%EA%B3%B5%EB%B6%80%ED%95%98%EB%8A%94-%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-%EB%94%A5%EB%9F%AC%EB%8B%9DChapter04.-%EB%8B%A4%EC%96%91%ED%95%9C-%EB%B6%84%EB%A5%98-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98</link>
            <guid>https://velog.io/@h_rin/%ED%98%BC%EC%9E%90-%EA%B3%B5%EB%B6%80%ED%95%98%EB%8A%94-%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-%EB%94%A5%EB%9F%AC%EB%8B%9DChapter04.-%EB%8B%A4%EC%96%91%ED%95%9C-%EB%B6%84%EB%A5%98-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98</guid>
            <pubDate>Sun, 03 Nov 2024 10:58:59 GMT</pubDate>
            <description><![CDATA[<h1 id="04-1-로지스틱-회귀">04-1 로지스틱 회귀</h1>
<h2 id="키워드">[키워드]</h2>
<h3 id="로지스틱-회귀">로지스틱 회귀</h3>
<p>: 선형방정식을 사용한 분류 알고리즘</p>
<ul>
<li>선형회귀와 달리 시그모이드 함수나 소프트 맥스 함수를 사용하여 클래스 확률을 출력함</li>
</ul>
<h3 id="다중-분류">다중 분류</h3>
<p>: 타깃 클래스가 2개 이상인 분류 문제</p>
<ul>
<li>로지스틱 회귀는 다중 분류를 위해 소프트 맥스 함수를 사용하여 클래스를 예측함</li>
</ul>
<h3 id="시그모이드-함수">시그모이드 함수</h3>
<p>: 선형 방정식의 출력을 0과 1사이의 값으로 압축하며 이진 분류를 위해 사용</p>
<p><img src="https://velog.velcdn.com/images/h_rin/post/650b6a93-ec01-4bbb-8c06-dc78f65d48a7/image.png" alt=""></p>
<h3 id="소프트-맥스-함수">소프트 맥스 함수</h3>
<p>: 다중 분류에서 여러 선형 방정식의 출력 결과를 정규화하여 합이 1이 되도록 함
즉, 시그모이드 함수는 하나의 선형 방정식의 출력값을 0<del>1사이로 압축하지만 소프트맥스 함수는 여러 개의 선형 방정식의 출력값을 0</del>1사이로 랍축하고 전체 합이 1이 되도록 만듦 -&gt; 지수함수 사용</p>
<p><img src="https://velog.velcdn.com/images/h_rin/post/201b3780-e738-420b-998b-69d830426697/image.png" alt=""></p>
<h2 id="핵심-패키지와-함수">[핵심 패키지와 함수]</h2>
<h3 id="scikit-learn">scikit-learn</h3>
<hr>

<h3 id="logisticrogression-클래스">LogisticRogression 클래스</h3>
<p>:선형 분류 알고리즘인 로지스틱 회귀를 위한 클래스</p>
<p>[주요 하이퍼 파라미터]</p>
<ul>
<li><p><code>solver</code>: 사용할 알고리즘 선택</p>
<blockquote>
<p>lbfgs가 기본</p>
</blockquote>
<p>lbfgs: 메모리 공간 절약, CPU 코어 수 많으면 최적화를 병렬로 수행
saga: 확률적 평균 경사 하강법 알고리즘으로 특성과 샘플 수가 많을 때 성능 good</p>
</li>
<li><p><code>penalty</code>: 규제방식 선택</p>
<blockquote>
<p>L2 규제: 릿지 방식
L1 규제: 라쏘 방식</p>
</blockquote>
</li>
<li><p><code>C</code>: 규제의 강도를 제어</p>
<ul>
<li>기본값은 1.0, 값이 작을 수록 규제 강해짐</li>
</ul>
</li>
</ul>
<h3 id="predict_proba-메서드">predict_proba() 메서드</h3>
<p>: 예측 확률 반환</p>
<ul>
<li>이중 분류에서는 샘플마다 음성 클래스와 양성 클래스에 대한 확률 반환</li>
<li>다중 분휴에서는 샘플마다 모든 클래스에 대한 확률 반환</li>
</ul>
<h3 id="decision_function-메서드">decision_function() 메서드</h3>
<p>: 모델이 학습한 선형 방정식의 출력 반환</p>
<ul>
<li>이진 분류의 경우 양성 클래스의 확률이 반환됨<ul>
<li>값이 0보다 크면 양성 클래스, 작거나 같으면 음성 클래스</li>
</ul>
</li>
<li>다중 분류의 경우 각 클래스마다 선형 방정식을 계산<ul>
<li>가장 큰 값의 클래스가 예특 클래스가 됨</li>
</ul>
</li>
</ul>
<h2 id="실습-데이터-준비하기">[실습] 데이터 준비하기</h2>
<pre><code>import pandas as pd

fish = pd.read_csv(&#39;https://bit.ly/fish_csv_data&#39;)
fish.head() # 제대로 불러 왔는지 보기 위해 처음 5행 출력</code></pre><p><img src="https://velog.velcdn.com/images/h_rin/post/f282123b-9dd3-4e15-8194-678a3f81662f/image.png" alt=""></p>
<pre><code>print(pd.unique(fish[&#39;Species&#39;]))


-&gt; [&#39;Bream&#39; &#39;Roach&#39; &#39;Whitefish&#39; &#39;Parkki&#39; &#39;Perch&#39; &#39;Pike&#39; &#39;Smelt&#39;]</code></pre><ul>
<li><code>.unique()</code>: 중복되는 거 빼고 고유한 값을 보여줌</li>
</ul>
<pre><code># 입력 데이터 설정
fish_input = fish[[&#39;Weight&#39;,&#39;Length&#39;,&#39;Diagonal&#39;,&#39;Height&#39;,&#39;Width&#39;]].to_numpy()

print(fish_input[:5])

-&gt;
[[242.      25.4     30.      11.52     4.02  ]
 [290.      26.3     31.2     12.48     4.3056]
 [340.      26.5     31.1     12.3778   4.6961]
 [363.      29.      33.5     12.73     4.4555]
 [430.      29.      34.      12.444    5.134 ]]

# 타깃 데이터 설정
fish_target = fish[&#39;Species&#39;].to_numpy()
</code></pre><pre><code># 훈련/테스트 세트 나눠줌
from sklearn.model_selection import train_test_split

train_input, test_input, train_target, test_target = train_test_split(
    fish_input, fish_target, random_state=42)

# 표준 점수로 전처리
from sklearn.preprocessing import StandardScaler

ss = StandardScaler()
ss.fit(train_input)
train_scaled = ss.transform(train_input)
test_scaled = ss.transform(test_input)</code></pre><h3 id="k-최근접-이웃-분류기의-확률-예측">k-최근접 이웃 분류기의 확률 예측</h3>
<ul>
<li>모델 훈련시키기<pre><code>from sklearn.neighbors import KNeighborsClassifier
</code></pre></li>
</ul>
<p>kn = KNeighborsClassifier(n_neighbors=3) # 이웃 3개
kn.fit(train_scaled, train_target)</p>
<p>print(kn.score(train_scaled, train_target))
print(kn.score(test_scaled, test_target))</p>
<h1 id="점수는-현재-별로-안중요">점수는 현재 별로 안중요</h1>
<p>-&gt;
0.8907563025210085
0.85</p>
<pre><code></code></pre><p>print(kn.classes_)</p>
<p>-&gt;
[&#39;Bream&#39; &#39;Parkki&#39; &#39;Perch&#39; &#39;Pike&#39; &#39;Roach&#39; &#39;Smelt&#39; &#39;Whitefish&#39;]</p>
<p>print(kn.predict(test_scaled[:5]))</p>
<p>-&gt;
[&#39;Perch&#39; &#39;Smelt&#39; &#39;Pike&#39; &#39;Perch&#39; &#39;Perch&#39;]</p>
<pre><code>- 타깃값을 그대로 사이킷런 모델에 전달시, 자동으로 알파벳 순서로 정렬됨
- KNeighborsClassifier에서 정렬된 타깃값은 `.classes_`에 저장됨
</code></pre><p>import numpy as np</p>
<p>proba = kn.predict_proba(test_scaled[:5])
print(np.round(proba, decimals=4))</p>
<p>-&gt;
[[0.     0.     1.     0.     0.     0.     0.    ]
 [0.     0.     0.     0.     0.     1.     0.    ]
 [0.     0.     0.     1.     0.     0.     0.    ]
 [0.     0.     0.6667 0.     0.3333 0.     0.    ]
 [0.     0.     0.6667 0.     0.3333 0.     0.    ]]</p>
<p>distances, indexes = kn.kneighbors(test_scaled[3:4])
print(train_target[indexes])</p>
<h1 id="위-5개-중에-4번째-샘플의-인덱스-받아서-모델의-실제-이웃들-확인">위 5개 중에 4번째 샘플의 인덱스 받아서 모델의 실제 이웃들 확인</h1>
<p>-&gt;
[[&#39;Roach&#39; &#39;Perch&#39; &#39;Perch&#39;]]</p>
<pre><code>- `.predict_proba()` : 예측에 대한 확률값을 계산해주는 메소드
- `np.round` : 소수점 첫째자리에서 반올림해주는 넘파이 함수
- `decimals` : 반올림(round)의 자릿수를 지정해주는 매개변수 

ex) 4번째 샘플 : Perch일 확률이 66%, Roach일 확률이 33%였음

-&gt; 가까운 이웃을 구해보니 일치 -&gt; `예측확률`

하지만 이 경우 가능한 확률이 0, 1/3, 2/3, 3/3 뿐임

### 로지스틱 회귀</code></pre><p>import numpy as np
import matplotlib.pyplot as plt</p>
<p>z = np.arange(-5, 5, 0.1)
phi = 1 / (1 + np.exp(-z))</p>
<p>plt.plot(z, phi)
plt.xlabel(&#39;z&#39;)
plt.ylabel(&#39;phi&#39;)
plt.show()</p>
<pre><code>![](https://velog.velcdn.com/images/h_rin/post/523f8a81-5226-4abd-9d87-b5b913d8cf4a/image.png)

#### 로지스틱 회귀로 이진분류하기

&gt; z=a×(무게)+b×(길이)+c×(대각선)+d×(높이)+e×(두께)+f

: z를 그대로 사용하면 `회귀`가 되지만 시그모이드 함수에 넣으면 0~1사이의 값이 됨 즉, 확률처럼 볼 수 있음 
</code></pre><p>char_arr = np.array([&#39;A&#39;, &#39;B&#39;, &#39;C&#39;, &#39;D&#39;, &#39;E&#39;])
print(char_arr[[True, False, True, False, False]])</p>
<p>-&gt;
[&#39;A&#39; &#39;C&#39;]</p>
<pre><code>
[데이터 준비 및 모델 훈련]
- 2마리(도미, 빙어)로 이진 분류 연습하자
    - 불리언 인덱싱(True, False로 분류)으로 Bream, Smelt행만 골라내기
</code></pre><h1 id="bream과-smelt인-타깃들만-true로-하고">Bream과 Smelt인 타깃들만 True로 하고</h1>
<p>bream_smelt_indexes = (train_target == &#39;Bream&#39;) | (train_target == &#39;Smelt&#39;)
train_bream_smelt = train_scaled[bream_smelt_indexes]
target_bream_smelt = train_target[bream_smelt_indexes]</p>
<h1 id="위에서-true로-바뀐-것들의-인덱스만-갖고-옴-이진-분류시-사용할-데이터-준비-과정">위에서 True로 바뀐 것들의 인덱스만 갖고 옴, 이진 분류시 사용할 데이터 준비 과정</h1>
<h1 id="로지스틱-회귀모델-객체-만들기">로지스틱 회귀모델 객체 만들기</h1>
<p>from sklearn.linear_model import LogisticRegression</p>
<p>lr = LogisticRegression()
lr.fit(train_bream_smelt, target_bream_smelt)</p>
<pre><code>
[샘플 예측하기]</code></pre><h1 id="처음-5개-샘플로-예측-해봄">처음 5개 샘플로 예측 해봄</h1>
<p>print(lr.predict(train_bream_smelt[:5]))</p>
<p>-&gt;
[&#39;Bream&#39; &#39;Smelt&#39; &#39;Bream&#39; &#39;Bream&#39; &#39;Bream&#39;]</p>
<h1 id="예측-확률-구하기">예측 확률 구하기</h1>
<p>print(lr.predict_proba(train_bream_smelt[:5]))</p>
<p>-&gt;
[[0.99760007 0.00239993]
 [0.02737325 0.97262675]
 [0.99486386 0.00513614]
 [0.98585047 0.01414953]
 [0.99767419 0.00232581]]</p>
<pre><code>&gt; 이진 분류에서 1열 = 음성 클래스, 2열 = 양성 클래스

사이킷런에서 타겟값을 알파벳 순으로 정렬하기 때문에 빙어는 양성, 도미는 음성이 됨

- predict_proba()가 반환한 값을 보면 두번째 샘플만 양성 클래스인 빙어의 확률이 높음, 나머지는 모두 도미로 예측할 것임

-&gt; 이진 분류 성공~!
</code></pre><h1 id="로지스틱이-학습한-계수-확인">로지스틱이 학습한 계수 확인</h1>
<p>print(lr.coef_, lr.intercept_)</p>
<p>-&gt;
[[-0.40451732 -0.57582787 -0.66248158 -1.01329614 -0.73123131]] [-2.16172774]</p>
<pre><code>- 절편과 계수가 있기 때문에 회귀방정식 사용해볼 수 있음 
    → 방정식에 따라 샘플마다의 z값도 구할 수 있음

- `.decision_function()`:  LogisticRegression()에서 z값을 출력하는 메소드
</code></pre><h1 id="샘플-5개의-z값-출력하기">샘플 5개의 z값 출력하기</h1>
<p>decisions = lr.decision_function(train_bream_smelt[:5])
print(decisions)</p>
<p>-&gt;
[-6.02991358  3.57043428 -5.26630496 -4.24382314 -6.06135688] </p>
<pre><code>→ 이 z값을 시그모이드 함수에 넣으면 확률을 얻을 수 있음
</code></pre><p>from scipy.special import expit</p>
<p>print(expit(decisions))</p>
<p>-&gt;
[0.00239993 0.97262675 0.00513614 0.01414953 0.00232581]</p>
<pre><code>- `expit()` : 사이파이 라이브러리에서 제공하는 시그모이드 함수
- 이진분류에서 `decision_function`의 반환값은 양성클래스에 대한 z값임(음성 = 1-양성으로 계산)

#### 로지스틱 회귀로 다중분류하기
[모델 훈련]
- 7마리 분류하는 훈련
- 이진분류와 방식 동일
- `C` : (릿지의 alpha처럼) 규제의 양을 조절하는 매개변수. (기본값=1)
- `max_iter` : 충분한 훈련을 위해 반복횟수를 지정하는 매개변수 (기본값=100)
</code></pre><p>lr = LogisticRegression(C=20, max_iter=1000)
lr.fit(train_scaled, train_target)</p>
<p>print(lr.score(train_scaled, train_target))
print(lr.score(test_scaled, test_target))</p>
<p>-&gt;
0.9327731092436975
0.925</p>
<pre><code>
[샘플 예측하기]</code></pre><p>print(lr.predict(test_scaled[:5]))</p>
<p>-&gt;
[&#39;Perch&#39; &#39;Smelt&#39; &#39;Pike&#39; &#39;Roach&#39; &#39;Perch&#39;]</p>
<pre><code>테스트 세트의 처음 5개에 대한 샘플을 예측함

[예측확률 구하기]

![](https://velog.velcdn.com/images/h_rin/post/af1cec18-97c5-407c-bb9c-a1f58396138f/image.png)

&gt; 다중 분류에서 클래스 개수만큼 확률을 출력함

첫번째 샘플은 Bream일 확률 0 / Parkki일 확률 0.014 / Perch일 확률 0.841 / Pike일 확률 0 / Roach일 확률 0.136 / Smelt일 확률 0.007 / Whitefish일 확률 0.003 으로 봄
</code></pre><p>print(lr.coef_.shape, lr.intercept_.shape)</p>
<p>-&gt;
(7, 5) (7,)</p>
<pre><code>- 이 데이터는 5개의 특성 사용함 -&gt; coef_배열의 열은 5개
- 행이 7이 나옴, intercept_도 7나옴 
    -&gt; 즉, z값을 7개 계산한다는 의미
    - 다중 분류는 클래스마다 z값을 하나씩 계산함
        -&gt; 가장 높은 값은 z값을 출력하는 예측 클래스가 됨
</code></pre><p>decision = lr.decision_function(test_scaled[:5])
print(np.round(decision, decimals=2))</p>
<p>-&gt;
[[ -6.51   1.04   5.17  -2.76   3.34   0.35  -0.63]
 [-10.88   1.94   4.78  -2.42   2.99   7.84  -4.25]
 [ -4.34  -6.24   3.17   6.48   2.36   2.43  -3.87]
 [ -0.69   0.45   2.64  -1.21   3.26  -5.7    1.26]
 [ -6.4   -1.99   5.82  -0.13   3.5   -0.09  -0.7 ]]</p>
<pre><code>- 절편과 계수로 회귀방정식 사용
- 방정식이 7세트 나옴 -&gt; 샘플마다 z값도 7개씩 나옴
</code></pre><p>from scipy.special import softmax</p>
<p>proba = softmax(decision, axis=1)
print(np.round(proba, decimals=3))</p>
<p>-&gt;
[[0.    0.014 0.842 0.    0.135 0.007 0.003]
 [0.    0.003 0.044 0.    0.007 0.946 0.   ]
 [0.    0.    0.034 0.934 0.015 0.016 0.   ]
 [0.011 0.034 0.305 0.006 0.567 0.    0.076]
 [0.    0.    0.904 0.002 0.089 0.002 0.001]]</p>
<pre><code>
- 이진분류에서 확률을 시그모이드 함수로 계산했지만 다중 분류는 소프트 맥스 함수를 이용해 7개의 z값을 확률로 변환
- 다중분류는 이진분류(샘플당 z값 1개)와 달리 z값이 여러개라서 다 합치면 1을 넘어갈 수 있음
    -&gt; 이를 해결하는게 소프트맥스 함수
- `softmax()` : 여러 개의 선형방정식의 출력값(z)을 0~1 사이로 압축하고, 총합이 1이 되게 만드는 함수.

![](https://velog.velcdn.com/images/h_rin/post/77daf5fe-06ea-41f3-9f74-03799f0605b9/image.png)

[참고]
- 소프트맥스 함수의 axis: 소프트맥스 계산의 단위를 각 행마다 하기 위해서 설정

![](https://velog.velcdn.com/images/h_rin/post/59804979-0847-4a3f-ae40-6f387698e2b7/image.png)

![](https://velog.velcdn.com/images/h_rin/post/285bce35-59e2-4ab1-9629-a9008b4afe3b/image.png)



# 04-2 확률적 경사 하강법
## [키워드]
### 확률적 경사 하강법
: 훈련 세트에서 샘플 하나씩 꺼내 손실 함수의 경사를 따라 최적의 모델을 찾는 알고리즘

- 샘플을 하나씩 사용하지 않고 여러 개를 사용하면 미니배치 경사 하강법이 됨
    - 한 번에 전체 샘플을 사용하면 배치 경사 하강법이 됨

### 손실함수
: 확률 경사 하강법이 최적화될 대상
- 대부분의 문제에 잘 맞는 손실 함수는 이미 정의되어 있음
- 이진 분류에서는 로지스틱 회귀 손실 함수를 사용
- 다중 분류에서는 크로스엔트로피 손실 함수를 사용
- 회귀 문제에서는 평균 오차 손실 함수 사용

### 에포크
: 확률적 경사 하강법에서 전체 샘플을 모두 사용하는 한 번 반복을 의미
- 일반적으로 경사 하상법 알고리즘은 수십에서 수백 번의 에포크를 반복함
- 지나치게 에포크를 높이게 되면 그 학습 데이터셋에 과적합(Overfitting)되어 다른 데이터에 대해서 제대로 예측을 못할 수 있음

## 확률적 경사 하강법
### 확률적 경사 하강법(SGD)
- 대표적인 &#39;점진적 학습법&#39;, &#39;알고리즘&#39;이 아니라 이런 알고리즘을 &#39;최적화 하는 방법&#39;임
- `경사 하강`: 가장 가파른 경사를 따라 조금씩 내려감
- `확률적`: 전체 샘플을 사용하지 않고 훈련 세트에서 딱 하나를 랜덤하게 골라 훈련함 = 전체 샘플을 다 쓸 때 까지 계속 하나씩 꺼내면서 조금씩 하강

-&gt; 샘플을 다 사용했는데도 내려오지 못했다면
- 다시 샘플을 채워 넣고 같은 방법으로 내려가면 됨
- 에포크(epoch): 훈련세트를 한 번 모두 사용하는 과정
- 보통 수번백의 에포크를 거침

### 미니배치 경사 하강법(minibatch-GD)
- 한 개씩만 꺼내는게 아니라 여러개씩 꺼내서 경사하강법 가능
- 개수는 보통 2의 배수로 하는 것이 일반적

### 배치 경사 하강법(batch-GD)
- 아예 샘플을 모두 꺼내서 경사하강
- 가장 안정적인 방법, 메모리적 한계 때문에 잘 사용x
![](https://velog.velcdn.com/images/h_rin/post/b5074722-a5a4-4e54-aeb4-1b0e587490ac/image.png)
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[혼자 공부하는 머신러닝 딥러닝]Chapter03. 회귀 알고리즘과 모델 규제]]></title>
            <link>https://velog.io/@h_rin/%ED%98%BC%EC%9E%90-%EA%B3%B5%EB%B6%80%ED%95%98%EB%8A%94-%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-%EB%94%A5%EB%9F%AC%EB%8B%9DChapter03.-%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/@h_rin/%ED%98%BC%EC%9E%90-%EA%B3%B5%EB%B6%80%ED%95%98%EB%8A%94-%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-%EB%94%A5%EB%9F%AC%EB%8B%9DChapter03.-%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, 12 Oct 2024 06:38:16 GMT</pubDate>
            <description><![CDATA[<h2 id="03-1-k-최근접-이웃-회귀">03-1 k-최근접 이웃 회귀</h2>
<h4 id="회귀">회귀</h4>
<p>: 임의의 수치를 예측하는 문제
    ex) 농어의 무게 예측, 경제 성장률 예측, 배달 도착 시간 예측 등</p>
<ul>
<li>k-최근접 이웃 회귀 : k-최근접 이웃 알고리즘을 사용해 회귀 문제를 푸는 것
→ 가장 가까운 이웃 샘플을 찾고 이 샘플들의 타깃값을 평균하여 예측함
→ 예측하려는 샘플에서 가장 가까운 샘플 k개 선택(클래스 X, 수치 O)</li>
</ul>
<pre><code>#훈련 데이터 준비
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])

#산점도 그리기
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/h_rin/post/3281c0f9-a1fe-472a-a82a-4f41982e6517/image.png" alt=""></p>
<pre><code>#훈련세트,테스트세트 나누기
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><p>→ 사이킷런에 사용할 훈련 세트는 2차원 배열이어야 함</p>
<pre><code>#배열 재배치
test_array = np.array([1,2,3,4]) #이때 배열의 크기는 (4,)
#(2,2)크기로 바꾸기
test_array = test_array.reshape(2,2)</code></pre><p>→ 1차원배열을 2차원배열로 나타내기 위해 reshape() 메서드 사용</p>
<h4 id="결정계수r2">결정계수(R^2)</h4>
<p>: 대표적인 회귀 문제 성능 측정 도구
 → 1의 가까울수록 Good, 0에 가까울수록 Bad
 → 회귀 모델에서 정확도는 결정계수 값으로 평가함</p>
<blockquote>
<p>R^2 = 1 - (타깃_예측)^2의 합 / (타깃_평균)^2의 합</p>
</blockquote>
<pre><code>#객체를 생성하고 fit()메서드로 회귀 모델 훈련하기
from sklearn.neighbors import KNeighborsRegressor
knr = KNeighborsRegressor()
knr.fit(train_input, train_target)

#정확도 확인
print(knr.score(test_input, test_target))</code></pre><ul>
<li>KNeighborsRegressor: k-최근접 이웃 회귀 모델을 만드는 사이킷런 클래스
  → n_neighbors 매개변수로 이웃의 개수를 지정(기본값 5)
  → 다른 매개변수는 KNeighborsClassifier 클래스와 거의 동일함</li>
</ul>
<h4 id="과대적합과-과소적합">과대적합과 과소적합</h4>
<ul>
<li>과대적합 : 모델의 훈련 세트 성능이 데스트 성능보다 훨씬 높을 때  일어남 
즉, 훈련세트에서 정확도 Good, 테스트 세트 정확도 점수 Bad
  → 모델이 훈련 세트에 너무 집착해서 데이터에 내재된 거시적인 패턴을 감지 X </li>
<li>과소적합: 훈련 세트와 테스트 세트 성능이 모두 동일하게 낮거나 테스트 세트 성능이 오히려 높을 때 일어남
즉, 훈련 세트보다 테스트 세트 점수가 높거나 두 점수가 모두 낮은 경우
  → 모델이 너무 단순하여 훈련 세트에 적절히 훈련되지 않음
  → 이런 경우 더 복잡한 모델을 사용해 훈련 세트에 잘 맞는 모델을 만들어야 함</li>
</ul>
<h2 id="03-2-선형-회귀">03-2 선형 회귀</h2>
<p>: 특성과 타깃 사이의 관계를 가장 잘 나타내는 선형 방정식을 찾음
→ 특성이 하나면 직선 방정식이 됨</p>
<blockquote>
</blockquote>
<ul>
<li><p>선형 회귀가 찾은 특성과 타깃 사이의 관계는 선형 방정식의 계수 또는 가중치에 저장됨</p>
</li>
<li><p>머신러닝에서 가중치는 방정식의 시울기와 절편을 모두 의미하는 경우가 많음</p>
</li>
<li><p>모델파라미터
: 선형 회귀가 찾은 가중치처럼 머신러닝 모델이 특성에서 학습한 파라미터를 말함</p>
</li>
<li><p>다항회귀
: 다항식을 사용하여 특성ㄱ과 타깃 사이의 관계를 나타냄
→ 비선형일 수도 있지만 선형 회귀로 표현 가능</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[혼자 공부하는 머신러닝 딥러닝]Chapter02. 데이터 다루기]]></title>
            <link>https://velog.io/@h_rin/%ED%98%BC%EC%9E%90-%EA%B3%B5%EB%B6%80%ED%95%98%EB%8A%94-%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-%EB%94%A5%EB%9F%AC%EB%8B%9DChapter02.-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%8B%A4%EB%A3%A8%EA%B8%B0</link>
            <guid>https://velog.io/@h_rin/%ED%98%BC%EC%9E%90-%EA%B3%B5%EB%B6%80%ED%95%98%EB%8A%94-%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-%EB%94%A5%EB%9F%AC%EB%8B%9DChapter02.-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%8B%A4%EB%A3%A8%EA%B8%B0</guid>
            <pubDate>Fri, 04 Oct 2024 16:00:42 GMT</pubDate>
            <description><![CDATA[<h1 id="02-1-훈련-세트와-테스트-세트">02-1 훈련 세트와 테스트 세트</h1>
<h3 id="지도학습과-비지도-학습">지도학습과 비지도 학습</h3>
<ul>
<li><p>지도학습
: 입력과 타깃을 전달하여 모델을 훈련한 다음 새로운 데이터를 예측하는 데 활용함</p>
</li>
<li><p>비지도  학습
: 타깃 데이터 X, 무엇을 예측하는 것이 아니라 입력 데이터에서 어떤 특징을 찾는 데 주로 활용함</p>
</li>
</ul>
<h3 id="훈련-세트와-테스트-세트">훈련 세트와 테스트 세트</h3>
<ul>
<li><p>훈련 세트(train set)
: 알고리즘 훈련에 사용되는 데이터로 보통 훈련 세트가 클수록 좋음
-&gt; 테스트 세트를 제외한 모든 데이터를 사용</p>
</li>
<li><p>테스트 세트(test set)
: 알고리즘 평가에 사용되는 데이터</p>
</li>
<li><p>샘플(sample)
: 하나의 데이터</p>
</li>
<li><p>슬라이싱(slicing)
: 콜론(:)을 가운데 두고 인덱스의 범위를 지정하여 여러 개의 원소를 선택할 수 있음
: 주의! 마지막 인덱스의 원소는 포함되지 않음</p>
</li>
</ul>
<h3 id="샘플링-편향">샘플링 편향</h3>
<p>: 일반적으로 훈련 세트와 테스트 세트에 샘플이 골고루 섞여 있지 않으면 샘플링이 한쪽으로 치우쳐 졌다는 의미</p>
<h3 id="numpy">numpy</h3>
<p>: 파이썬의 대표적인 배열 라이브러리</p>
<ul>
<li><p>seed()
:넘파이에서 난수를 생성하기 위한 정수 초깃값 지정, 초깃값이 같으면 동일한 난수 생성 가능</p>
</li>
<li><p>arange()
: 일정한 간격의 정수 또는 실수 배열을 만듦, 기본 간격은 1</p>
</li>
<li><blockquote>
<p>매개변수가 하나이면 종료 숫자를 의미함, 0에서 종료 숫자까지 배열을 만듦(단, 종료 숫자는 배열에 포함 X)</p>
</blockquote>
</li>
</ul>
<pre><code>print(np.arange(3)) #[0, 1, 2]
print(np.arange(1, 3)) #[1, 2] (시작 숫자, 종료 숫자)
print(np.arange(1, 2, 0.2)) #[1., 1.2, 1.4, 1.6, 1.8] (시작 숫자, 종료 숫자, 간격)</code></pre><ul>
<li>shuffle()
:주어진 배열을 랜덤하게 섞음, 다차원 배열일 경우 첫 번째 축(행)에 대해서만 섞음</li>
</ul>
<pre><code>arr = np.array([[1, 2], [3, 4], [5, 6]])
np.random.shuffle(arr)
print(arr)

&quot;&quot;&quot;
[[3 4]
 [5 6]
 [1 2]]
&quot;&quot;&quot;</code></pre><p>[전체 코드]</p>
<pre><code>## 훈련 세트와 테스트 세트

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]

fish_data = [[l, w] for l, w in zip(fish_length, fish_weight)]
fish_target = [1]*35 + [0]*14

from sklearn.neighbors import KNeighborsClassifier

kn = KNeighborsClassifier()

print(fish_data[4])

print(fish_data[0:5])

print(fish_data[:5])

print(fish_data[44:])

train_input = fish_data[:35]
train_target = fish_target[:35]

test_input = fish_data[35:]
test_target = fish_target[35:]

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

## 넘파이

import numpy as np

input_arr = np.array(fish_data)
target_arr = np.array(fish_target)

print(input_arr)

print(input_arr.shape)

np.random.seed(42)
index = np.arange(49)
np.random.shuffle(index)

print(index)

print(input_arr[[1,3]])

train_input = input_arr[index[:35]]
train_target = target_arr[index[:35]]

print(input_arr[13], train_input[0])

test_input = input_arr[index[35:]]
test_target = target_arr[index[35:]]

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()

## 두 번째 머신러닝 프로그램

kn = kn.fit(train_input, train_target)

kn.score(test_input, test_target)

kn.predict(test_input)

test_target</code></pre><p><strong>[출력]</strong>
[29.0, 430.0]
[[25.4, 242.0], [26.3, 290.0], [26.5, 340.0], [29.0, 363.0], [29.0, 430.0]]
[[25.4, 242.0], [26.3, 290.0], [26.5, 340.0], [29.0, 363.0], [29.0, 430.0]]
[[12.2, 12.2], [12.4, 13.4], [13.0, 12.2], [14.3, 19.7], [15.0, 19.9]]
[[  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]]
(49, 2)
[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]
[[ 26.3 290. ]
 [ 29.  363. ]]
[ 32. 340.] [ 32. 340.]
<img src="https://velog.velcdn.com/images/h_rin/post/fe86d3f2-8d95-407e-a3b4-9312b86f5546/image.png" alt="">
array([0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0])</p>
<h1 id="02-2-데이터-전처리">02-2 데이터 전처리</h1>
<ul>
<li><p>데이터 전처리
: 머신러닝 모델에 훈련 데이터를 주입하기 전에 가공하는 단계</p>
</li>
<li><p>표준점수
: 훈련 세트의 스케일을 바꾸는 대표적인 방법 중 하나, 이를 얻기 위해서는 특성의 평균을 빼고 표준편차로 나눔
: 반드시 훈련 세트의 평균과 표준편차로 테스트 세트를 바꿔야 함</p>
</li>
<li><p>브로드캐스팅
: 크기가 다른 넘파이 배열에서 자동으로 사칙 연산을 모든 행이나 열로 확장하여 수행하는 기능</p>
</li>
</ul>
<h3 id="scikit-learn">scikit-learn</h3>
<ul>
<li><p>train_test_split()
: 훈련 데이터를 훈련 세트와 테스트 세트로 나누는 함수</p>
<blockquote>
<ul>
<li>여러 개의 배열을 전달할 수 있음</li>
<li>test_size 매개변수에서 테스트 세트로 나눌 비율을 지정 가능(기본값 0.25)</li>
<li>shuffle 매개변수로 훈련 세트와 테스트 세트로 나누기 전에 무작위로 섞을 지 여부를 결정할 수 있음(기본값 True)</li>
<li>stratify 매개변수에 클래스 레이블이 담긴 배열을 전달하면 클래스 비율에 맞게 훈련 세트와 테스트 세트를 나눔</li>
</ul>
</blockquote>
</li>
<li><p>kneighbors()
: k-최근접 이웃 객체의 메서드, 입력한 데이터에 가장 가까운 이웃을 찾아 거리와 이웃 샘플의 인덱스를 반환함</p>
<blockquote>
<ul>
<li>기본적으로 이웃의 개수는 KNeighborsClassifier 클래스의 객체를 생성할 때 지정한 개수를 사용</li>
</ul>
</blockquote>
<ul>
<li>n_neighbors 매개변수에서 다르게 지정 가능<blockquote>
<ul>
<li>return_distance 매개변수를 False로 지정하면 이웃 샘플의 인덱스만 반환하고 거리는 반환 X (기본값 True)</li>
</ul>
</blockquote>
</li>
</ul>
</li>
</ul>
<p>**[전체 코드] **   </p>
<pre><code># 데이터 전처리

## 넘파이로 데이터 준비하기

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]

import numpy as np

np.column_stack(([1,2,3], [4,5,6]))

fish_data = np.column_stack((fish_length, fish_weight))

print(fish_data[:5])

print(np.ones(5))

fish_target = np.concatenate((np.ones(35), np.zeros(14)))

print(fish_target)

## 사이킷런으로 훈련 세트와 테스트 세트 나누기

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)

print(train_input.shape, test_input.shape)

print(train_target.shape, test_target.shape)

print(test_target)

train_input, test_input, train_target, test_target = train_test_split(
    fish_data, fish_target, stratify=fish_target, random_state=42)

print(test_target)

## 수상한 도미 한마리

from sklearn.neighbors import KNeighborsClassifier

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

print(kn.predict([[25, 150]]))

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()

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()

print(train_input[indexes])

print(train_target[indexes])

print(distances)

## 기준을 맞춰라

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()

mean = np.mean(train_input, axis=0)
std = np.std(train_input, axis=0)

print(mean, std)

train_scaled = (train_input - mean) / std

## 전처리 데이터로 모델 훈련하기

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()

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()

kn.fit(train_scaled, train_target)

test_scaled = (test_input - mean) / std

kn.score(test_scaled, test_target)

print(kn.predict([new]))

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><strong>[출력]</strong></p>
<p>[[ 25.4 242. ]
 [ 26.3 290. ]
 [ 26.5 340. ]
 [ 29.  363. ]
 [ 29.  430. ]]
[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.</p>
<ol>
<li><ol>
<li><ol>
<li><ol>
<li><ol>
<li><ol>
<li><ol>
<li><ol>
<li><ol>
<li><ol>
<li><ol>
<li><ol start="0">
<li><ol start="0">
<li><ol start="0">
<li><ol start="0">
<li><ol start="0">
<li><ol start="0">
<li><ol start="0">
<li><ol start="0">
<li><ol start="0">
<li><ol start="0">
<li><ol start="0">
<li><ol start="0">
<li>0.</li>
</ol>
</li>
</ol>
</li>
</ol>
</li>
</ol>
</li>
</ol>
</li>
</ol>
</li>
</ol>
</li>
</ol>
</li>
</ol>
</li>
</ol>
</li>
</ol>
</li>
</ol>
</li>
</ol>
</li>
</ol>
</li>
</ol>
</li>
</ol>
</li>
</ol>
</li>
</ol>
</li>
</ol>
</li>
</ol>
</li>
</ol>
</li>
</ol>
</li>
<li>]
(36, 2) (13, 2)
(36,) (13,)
[1. 0. 0. 0. 1. 1. 1. 1. 1. 1. 1. 1. 1.]
[0. 0. 1. 0. 1. 0. 1. 1. 1. 1. 1. 1. 1.]
[0.]
<img src="https://velog.velcdn.com/images/h_rin/post/c05bd693-0087-4bb6-a38b-5457d218e86d/image.png" alt="">
<img src="https://velog.velcdn.com/images/h_rin/post/e532dc3a-73f0-472e-8b6c-08d2eb595cb9/image.png" alt="">
[[[ 25.4 242. ]
[ 15.   19.9]
[ 14.3  19.7]
[ 13.   12.2]
[ 12.2  12.2]]]
[[1. 0. 0. 0. 0.]]
[[ 92.00086956 130.48375378 130.73859415 138.32150953 138.39320793]]
<img src="https://velog.velcdn.com/images/h_rin/post/edc2b2b3-c964-4a02-aa03-ff6ba45db690/image.png" alt="">
[ 27.29722222 454.09722222] [  9.98244253 323.29893931]
<img src="https://velog.velcdn.com/images/h_rin/post/4f0c3bca-a2f7-49cb-89d3-65849f86e119/image.png" alt="">
<img src="https://velog.velcdn.com/images/h_rin/post/555f98f7-2033-4051-a45c-900ff8d9c268/image.png" alt="">
<img src="https://velog.velcdn.com/images/h_rin/post/1ae320cb-8aa0-44f0-9491-c3d149204f57/image.png" alt=""></li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[혼자 공부하는 머신러닝 딥러닝]Chapter01. 나의 첫 머신러닝]]></title>
            <link>https://velog.io/@h_rin/%ED%98%BC%EC%9E%90-%EA%B3%B5%EB%B6%80%ED%95%98%EB%8A%94-%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-%EB%94%A5%EB%9F%AC%EB%8B%9DChapter01.-%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/@h_rin/%ED%98%BC%EC%9E%90-%EA%B3%B5%EB%B6%80%ED%95%98%EB%8A%94-%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-%EB%94%A5%EB%9F%AC%EB%8B%9DChapter01.-%EB%82%98%EC%9D%98-%EC%B2%AB-%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D</guid>
            <pubDate>Fri, 04 Oct 2024 14:57:55 GMT</pubDate>
            <description><![CDATA[<h1 id="01-1-인공지능과-머신러닝-딥러닝">01-1 인공지능과 머신러닝, 딥러닝</h1>
<p><img src="https://velog.velcdn.com/images/h_rin/post/a1819b22-eb1c-46dc-8dfd-ead033d90c6d/image.png" alt=""></p>
<h3 id="1-인공지능artificial-intelligence이란">1. 인공지능(artificial intelligence)이란</h3>
<p>: 사람처럼 학습하고 추론할 수 있는 지능을 가진 컴퓨터 시스템을 만드는 기술</p>
<blockquote>
<p><strong>인공일반지능(artificial general inteligence) or 강인공지능(Strong AI)</strong>
: 사람과 구분하기 어려운 지능을 가진 컴퓨터 시스템 </p>
</blockquote>
<blockquote>
<p><strong>약인공지능(Week AI)</strong>
: 현실에서 우리가 마주하고 있는 인공지능
  (현재까지 특정 분야에서 사람의 일을 도와주는 보조 역할만 가능함)</p>
</blockquote>
<h3 id="2-머신러닝machine-learning이란">2. 머신러닝(machine learning)이란</h3>
<p>: 규칙을 일일이 프로그래밍하지 않아도 자동으로 데이터에서 규칙을 학습하는 알고리즘을 연구하는 분야</p>
<ul>
<li>컴퓨터 과학 분야의 대표적인 머신러닝 라이브러리</li>
<li><blockquote>
<p>사이킷런(scikit-learn)</p>
</blockquote>
</li>
</ul>
<h3 id="3-딥러닝deep-learning이란">3. 딥러닝(Deep learning)이란</h3>
<p>: 인공 신경망(artificial neural network)을 기반으로 한 방법들을 통칭함</p>
<ul>
<li><p>최초의 합성곱 신경망: LeNet-5(손글씨 숫자를 인식)</p>
</li>
<li><p>이미지 분류 대회인 ImageNet에서 AlexNet이 압도적 성능으로 우승</p>
</li>
<li><p>구글의 딥러닝 라이브러리 오픈소스: TensorFlow</p>
</li>
<li><p>페이스북 딥러닝 라이브러리 오픈소스: PyTorch</p>
</li>
</ul>
<h1 id="01-2-코랩과-주피터-노트북">01-2 코랩과 주피터 노트북</h1>
<ul>
<li><p>코랩
: 웹브라우저 기반의 파이썬 코드 실행 환경</p>
</li>
<li><p>노트북
: 코랩의 프로그램 작성 단위이며 일반 프로그램 파일과 달리 대화식으로 프로그램을 만들 수 있음</p>
</li>
</ul>
<h1 id="01-3-마켓과-머신러닝">01-3 마켓과 머신러닝</h1>
<p><strong>[생선분류 문제]</strong>
머신러닝은 여러 개의 도미 데이터를 넣으면 스스로 어떤 생선이 도미인지 구분할 기준을 찾는다. </p>
<ul>
<li><p>특성
: 데이터를 표현하는 하나의 성질
ex) 생선의 길이와 무게</p>
</li>
<li><p>훈련
: 머신러닝 알고리즘이 데이터에서 규칙을 찾는 과정</p>
<blockquote>
<p>사이킷런 - fit()메서드 이용</p>
</blockquote>
</li>
<li><p>k-최근접 이웃 알고리즘
: 가장 간단한 머신러닝 알고리즘 중 하나로 주위의 다른 데이터를 보고 다수를 차지하는 것을 정답으로 사용</p>
<blockquote>
</blockquote>
<p>fit()메서드에 전달한 데이터를 모두 저장하고 있다가 새로운 데이터가 등장하면 가장 가까운 데이터를 참고하여 도미인지 빙어인지 구분함</p>
</li>
<li><p>모델
: 머신러닝 프로그램에서 알고리즘이 구현된 객체를 의미, 알고리즘 자체를 모델이라고 부르기도 함</p>
</li>
<li><p>정확도
: 정확한 답을 몇 개 맞혔는지 백분율로 나타낸 값(사이킷런 0~1)</p>
<blockquote>
<p>정확도 = (정확히 맞힌 개수) / (전체 데이터 개수)</p>
</blockquote>
</li>
</ul>
<h4 id="matplotlib">matplotlib</h4>
<ul>
<li>scatter()
:산점도를 그리는 함수, 처음 2개의 매개변수로 x축 값과 y축 값을 전달</li>
</ul>
<h4 id="scikit-learn">scikit-learn</h4>
<ul>
<li><p>KNeighborsClassifier()
: k-최근접 이웃 분류 모델을 만드는 사이킷런 클래스
: n_neighbors매개변수로 이웃의 개수를 지정함 -&gt; 기본값 5</p>
</li>
<li><p>fit()
: 사이킷런 모델을 훈련할 때 사용하는 메서드, 처음 두 매개변수로 훈련에 사용할 특성과 정답 데이터를 전달함</p>
</li>
<li><p>predict()
: 사이킷런 모델을 훈련하고 예측할 때 사용하는 메서드, 특성 데이터 하나만 매개변수로 받음 - 새로운 데이터 정답 예측</p>
</li>
<li><p>score()
: 훈련된 사이킷런 모델의 성능을 측정함, 처음 두 매개변수로 특성과 정답 데이터를 전달함 - 정확도
: 이 메서드는 먼저 predict() 메서드로 예측을 수행한 다음 분류 모델일 경우 정답과 비교하여 올바르게 예측한 개수의 비율을 반환함</p>
</li>
</ul>
<p>[전체 소스 코드]</p>
<pre><code># 마켓과 머신러닝
# 생선 분류 문제

# 도미 데이터 준비하기
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]

import matplotlib.pyplot as plt # matplotlib의 pylot 함수를 plt로 줄여서 사용

plt.scatter(bream_length, bream_weight)
plt.xlabel(&#39;length&#39;) # x축은 길이
plt.ylabel(&#39;weight&#39;) # y축은 무게
plt.show()

# 빙어 데이터 준비하기
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]

# 첫 번째 머신 러닝 프로그램

length = bream_length + smelt_length
weight = bream_weight + smelt_weight

fish_data = [[l, w] for l, w in zip(length, weight)]

print(fish_data)

fish_target = [1] * 35 + [0] * 14
print(fish_target)

from sklearn.neighbors import KNeighborsClassifier

kn =  KNeighborsClassifier()


kn.fit(fish_data, fish_target)


kn.score(fish_data, fish_target)


# k-최근접 이웃 알고리즘

plt.scatter(bream_length, bream_weight)
plt.scatter(smelt_length, smelt_weight)
plt.scatter(30, 600, marker= &#39;^&#39;)
plt.xlabel(&#39;length&#39;)
plt.ylabel(&#39;weight&#39;)
plt.show()

kn.predict([[30, 600]])

print(kn._fit_X)

print(kn._y)

kn49 =  KNeighborsClassifier(n_neighbors=49)


kn49.fit(fish_data, fish_target)
kn49.score(fish_data, fish_target)

print(35/49)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[Node.js]VCS로 만든 웹사이트 브라우저에서 창 열기]]></title>
            <link>https://velog.io/@h_rin/Node.jsVCS%EB%A1%9C-%EB%A7%8C%EB%93%A0-%EC%9B%B9%EC%82%AC%EC%9D%B4%ED%8A%B8-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80%EC%97%90%EC%84%9C-%EC%B0%BD-%EC%97%B4%EA%B8%B0</link>
            <guid>https://velog.io/@h_rin/Node.jsVCS%EB%A1%9C-%EB%A7%8C%EB%93%A0-%EC%9B%B9%EC%82%AC%EC%9D%B4%ED%8A%B8-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80%EC%97%90%EC%84%9C-%EC%B0%BD-%EC%97%B4%EA%B8%B0</guid>
            <pubDate>Mon, 23 Sep 2024 06:52:33 GMT</pubDate>
            <description><![CDATA[<ol>
<li><p>VSC의 터미널에서 node main.js(파일명)으로 실행시킨다.
<img src="https://velog.velcdn.com/images/h_rin/post/961867e3-0bfc-4b9e-bbfa-ba5599bc13b3/image.png" alt=""></p>
</li>
<li><p>코드 내에서 설정해준 포트(port)번호를 확인한다.
<img src="https://velog.velcdn.com/images/h_rin/post/6890aae5-50ed-4c6f-a102-c66d81e93a3d/image.png" alt=""></p>
</li>
<li><p>브라우저 주소창에 localhost:(port번호)를 입력하면 js 파일이 브라우저에 나타난다!
<img src="https://velog.velcdn.com/images/h_rin/post/77a8cd43-9a78-4148-9d98-0b1e2538585f/image.png" alt=""></p>
</li>
</ol>
<p>끄읕.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[생활코딩]Node.js : 13-23강]]></title>
            <link>https://velog.io/@h_rin/%EC%83%9D%ED%99%9C%EC%BD%94%EB%94%A9Node.js-13-23%EA%B0%95</link>
            <guid>https://velog.io/@h_rin/%EC%83%9D%ED%99%9C%EC%BD%94%EB%94%A9Node.js-13-23%EA%B0%95</guid>
            <pubDate>Mon, 23 Sep 2024 06:42:22 GMT</pubDate>
            <description><![CDATA[<h2 id="13-app-제작---파일을-이용해-본문-구현">13. App 제작 - 파일을 이용해 본문 구현</h2>
<p>: 폴더 내부에 data 폴더 생성 → <code>HTML</code>, <code>CSS</code>, <code>JavaScript</code> 각 파일 생성(확장자x) → 본문 <code>&lt;p&gt;</code>태그 내용들 파일에 넣어줌</p>
<pre><code class="language-jsx">var http = require(&#39;http&#39;);
var fs = require(&#39;fs&#39;);
var url = require(&#39;url&#39;);

var app = http.createServer(function(request,response){
    var _url = request.url;
    var queryData = url.parse(_url, true).query;
    var title = queryData.id;
    if(_url == &#39;/&#39;){
      title = &#39;Welcome&#39;;
    }
    if(_url == &#39;/favicon.ico&#39;){
      return response.writeHead(404);
    }
    response.writeHead(200);
    fs.readFile(`data/${queryData.id}`, &#39;utf8&#39;, function(err, description){

        var template = `
        &lt;!doctype html&gt;
        &lt;html&gt;
        &lt;head&gt;
        &lt;title&gt;WEB1 - ${title}&lt;/title&gt;
        &lt;meta charset=&quot;utf-8&quot;&gt;
        &lt;/head&gt;
        &lt;body&gt;
        &lt;h1&gt;&lt;a href=&quot;/&quot;&gt;WEB&lt;/a&gt;&lt;/h1&gt;
        &lt;ul&gt;
            &lt;li&gt;&lt;a href=&quot;/?id=HTML&quot;&gt;HTML&lt;/a&gt;&lt;/li&gt;
            &lt;li&gt;&lt;a href=&quot;/?id=CSS&quot;&gt;CSS&lt;/a&gt;&lt;/li&gt;
            &lt;li&gt;&lt;a href=&quot;/?id=JavaScript&quot;&gt;JavaScript&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
        &lt;h2&gt;${title}&lt;/h2&gt;
        &lt;p&gt;${description}&lt;/p&gt;
        &lt;/body&gt;
        &lt;/html&gt;
        `;
        response.end(template);

    })

});
app.listen(3000);</code></pre>
<ul>
<li>fs.readFile(<code>data/${queryData.id}</code>, &#39;utf8&#39;, function(err, description){  }
  → data파일 속에 있는 <code>${queryData.id}</code> 파일 경로에 맞게 내용을 <code>${description}</code> 속으로 가지고 온다. </li>
</ul>
<h2 id="14-17-javascriptboolean-비교연산자-제어문-조건문">14-17. JavaScript(Boolean, 비교연산자, 제어문, 조건문)</h2>
<ul>
<li><p>Boolean</p>
<pre><code class="language-jsx">  console.log(true); // 1
  console.log(false);</code></pre>
</li>
<li><p>비교연산자(Comparison operator)</p>
<pre><code class="language-jsx">  console.log(1==1); // true
  console.log(1==2); // false(값 비교)
  console.log(1&gt;2); // false
  console.log(1&lt;2); // false
  console.log(1===1); // true
  console.log(1===2); // false(값과 데이터 타입 비교)</code></pre>
<ul>
<li>==과 ===의 차이<ul>
<li>==는 서로의 <code>값</code>이 같은지 비교</li>
<li>===은 <code>값</code>과 <code>데이터 타입</code>이 같은지 비교</li>
</ul>
</li>
</ul>
</li>
<li><p>제어문(Flow control statements)
: 여러 프로그램을 수행하거나 수행하지 않도록 하거나, 특정 문장을 여러 번 반복 수행하게 함.</p>
<p>  ex) 조건문, 반복문 등</p>
<ul>
<li>Program = 어떤 일의 진행 목록이나 순서, 차례 등을 뜻함 </li>
</ul>
</li>
<li><p>조건문(Conditional statements)</p>
<pre><code class="language-jsx">  console.log(&#39;A&#39;);
  console.log(&#39;B&#39;);
  if(false){
    console.log(&#39;C1&#39;);
  } else {
    console.log(&#39;C2&#39;);
  }
  console.log(&#39;D&#39;);
  // 기본 true라 C2 출력됨</code></pre>
</li>
</ul>
<h2 id="18-nodejs---콘솔에서의-입력값">18. Node.js - 콘솔에서의 입력값</h2>
<pre><code class="language-jsx">var args = process.argv; //js는 내용을 배열로 저장함
console.log(args[2]);
console.log(&#39;A&#39;);
console.log(&#39;B&#39;);
if(args[2] === &#39;1&#39;){
  console.log(&#39;C1&#39;);
} else {
  console.log(&#39;C2&#39;);
}
console.log(&#39;D&#39;);</code></pre>
<h2 id="19-1-app-제작---not-found-구현">19-1. App 제작 - Not found 구현</h2>
<pre><code class="language-jsx">var http = require(&#39;http&#39;);
var fs = require(&#39;fs&#39;);
var url = require(&#39;url&#39;);

var app = http.createServer(function(request,response){
    var _url = request.url;
    var queryData = url.parse(_url, true).query;
    var title = queryData.id;
    var pathname = url.parse(_url, true).pathname; // pathname으로 변경

    if(pathname === &#39;/&#39;){  // if-else문으로 변경
      fs.readFile(`data/${queryData.id}`, &#39;utf8&#39;, function(err, description){

        var template = `
        &lt;!doctype html&gt;
        &lt;html&gt;
        &lt;head&gt;
        &lt;title&gt;WEB1 - ${title}&lt;/title&gt;
        &lt;meta charset=&quot;utf-8&quot;&gt;
        &lt;/head&gt;
        &lt;body&gt;
        &lt;h1&gt;&lt;a href=&quot;/&quot;&gt;WEB&lt;/a&gt;&lt;/h1&gt;
        &lt;ul&gt;
            &lt;li&gt;&lt;a href=&quot;/?id=HTML&quot;&gt;HTML&lt;/a&gt;&lt;/li&gt;
            &lt;li&gt;&lt;a href=&quot;/?id=CSS&quot;&gt;CSS&lt;/a&gt;&lt;/li&gt;
            &lt;li&gt;&lt;a href=&quot;/?id=JavaScript&quot;&gt;JavaScript&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
        &lt;h2&gt;${title}&lt;/h2&gt;
        &lt;p&gt;${description}&lt;/p&gt;
        &lt;/body&gt;
        &lt;/html&gt;
        `;
        response.writeHead(200);
        response.end(template);

      })
    }else{ //없는 페이지일 경우 Not found
      response.writeHead(404);
      response.end(&#39;Not found&#39;);
    }


});
app.listen(3000);</code></pre>
<ul>
<li>url.parse(_url, true) : 반환된 <code>Url</code> 객체에서 <code>pathname</code>프로퍼티는 쿼리스트링을 제외한 path만을 보여줌</li>
</ul>
<p>ex) pathname: ‘/’, path: ‘/?id=HTML’</p>
<h2 id="19-2-app-제작---홈페이지-구현">19-2. App 제작 - 홈페이지 구현</h2>
<pre><code class="language-jsx">var http = require(&#39;http&#39;);
var fs = require(&#39;fs&#39;);
var url = require(&#39;url&#39;);

var app = http.createServer(function(request,response){
    var _url = request.url;
    var queryData = url.parse(_url, true).query;
    var pathname = url.parse(_url, true).pathname;

    if(pathname === &#39;/&#39;){
      if(queryData.id === undefined){ // if-else문으로 메인페이지를 나타낼 수 있도록 수정함.
        fs.readFile(`data/${queryData.id}`, &#39;utf8&#39;, function(err, description){
          var title = &#39;Welcome&#39;; //여기
          var description = &#39;Hello, Node.js&#39;; //여기 수정
          var template = `
          &lt;!doctype html&gt;
          &lt;html&gt;
          &lt;head&gt;
          &lt;title&gt;WEB1 - ${title}&lt;/title&gt;
          &lt;meta charset=&quot;utf-8&quot;&gt;
          &lt;/head&gt;
          &lt;body&gt;
          &lt;h1&gt;&lt;a href=&quot;/&quot;&gt;WEB&lt;/a&gt;&lt;/h1&gt;
          &lt;ul&gt;
              &lt;li&gt;&lt;a href=&quot;/?id=HTML&quot;&gt;HTML&lt;/a&gt;&lt;/li&gt;
              &lt;li&gt;&lt;a href=&quot;/?id=CSS&quot;&gt;CSS&lt;/a&gt;&lt;/li&gt;
              &lt;li&gt;&lt;a href=&quot;/?id=JavaScript&quot;&gt;JavaScript&lt;/a&gt;&lt;/li&gt;
          &lt;/ul&gt;
          &lt;h2&gt;${title}&lt;/h2&gt;
          &lt;p&gt;${description}&lt;/p&gt;
          &lt;/body&gt;
          &lt;/html&gt;
          `;
          response.writeHead(200);
          response.end(template);

        })
      }else{
        fs.readFile(`data/${queryData.id}`, &#39;utf8&#39;, function(err, description){
          var title = queryData.id; //원래 if문 밖에서 선언했는데 안으로 옮김
          var template = `
          &lt;!doctype html&gt;
          &lt;html&gt;
          &lt;head&gt;
          &lt;title&gt;WEB1 - ${title}&lt;/title&gt;
          &lt;meta charset=&quot;utf-8&quot;&gt;
          &lt;/head&gt;
          &lt;body&gt;
          &lt;h1&gt;&lt;a href=&quot;/&quot;&gt;WEB&lt;/a&gt;&lt;/h1&gt;
          &lt;ul&gt;
              &lt;li&gt;&lt;a href=&quot;/?id=HTML&quot;&gt;HTML&lt;/a&gt;&lt;/li&gt;
              &lt;li&gt;&lt;a href=&quot;/?id=CSS&quot;&gt;CSS&lt;/a&gt;&lt;/li&gt;
              &lt;li&gt;&lt;a href=&quot;/?id=JavaScript&quot;&gt;JavaScript&lt;/a&gt;&lt;/li&gt;
          &lt;/ul&gt;
          &lt;h2&gt;${title}&lt;/h2&gt;
          &lt;p&gt;${description}&lt;/p&gt;
          &lt;/body&gt;
          &lt;/html&gt;
          `;
          response.writeHead(200);
          response.end(template);

        })
      }
    }else{
      response.writeHead(404);
      response.end(&#39;Not found&#39;);
    }
});
app.listen(3000);</code></pre>
<p>홈페이지에 undefined 뜨던 것을 해결함. </p>
<p>→ if(queryData.id === undefined) 조건으로 한 번 더 나눠줌.</p>
<h2 id="20-22-javascript반복문-배열-배열과-반복문">20-22. JavaScript(반복문, 배열, 배열과 반복문)</h2>
<ul>
<li><p>반복문(Loop statement)</p>
<pre><code class="language-jsx">  console.log(&#39;A&#39;);
  console.log(&#39;B&#39;);

  var i = 0;
  while(i &lt; 2){
    console.log(&#39;C1&#39;);
    console.log(&#39;C2&#39;);
    i = i + 1;
  }

  console.log(&#39;D&#39;);</code></pre>
</li>
<li><p>배열(Array)</p>
<pre><code class="language-jsx">  var arr = [&#39;A&#39;,&#39;B&#39;,&#39;C&#39;,&#39;D&#39;];
  console.log(arr[1]);
  console.log(arr[3]);
  arr[2] = 3; // 인덱스 번호 2번째 원소 3으로 바꿈
  console.log(arr); // 배열 전체 출력
  console.log(arr.length); // 배열의 크기
  arr.push(&#39;E&#39;); // 배열의 끝에 추가함
  console.log(arr);</code></pre>
</li>
</ul>
<ul>
<li><p>배열과 반복문</p>
<p>  : 배열 내에 있는 숫자들의 합을 나타내는 코드</p>
<pre><code class="language-jsx">  var number = [1,400,12,34];
  var i = 0;
  var total = 0;
  while(i &lt; number.length){
    total = total + number[i];
    i = i + 1;
  }
  console.log(`total : ${total}`);</code></pre>
</li>
</ul>
<h2 id="23-nodejs에서-파일목록-알아내기">23. Node.js에서 파일목록 알아내기</h2>
<pre><code class="language-jsx">var testFolder = &#39;./data&#39;; //파일 목록 읽어올 폴더
var fs = require(&#39;fs&#39;); // file system 모듈 가져옴

fs.readdir(testFolder, function(error, filelist){
  console.log(filelist);
})</code></pre>
<ul>
<li><p>fs.readFile(path [,options], callback) 메소드
: 파일을 읽어옴</p>
</li>
<li><p>fs.readdir(path [,options], callback) 메소드
: 디렉토리 목록을 읽어옴</p>
<p>  → 첫번째 인자(path) = 파일 목록을 읽을 폴더(dir) 가져옴</p>
<p>  → 콜백함수의 두번째 인자(filelist) = 폴더의 파일목록 가져옴</p>
</li>
</ul>
<p>끄읕.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[생활코딩]Node.js : 1-12강]]></title>
            <link>https://velog.io/@h_rin/%EC%83%9D%ED%99%9C%EC%BD%94%EB%94%A9Node.js-1-12%EA%B0%95</link>
            <guid>https://velog.io/@h_rin/%EC%83%9D%ED%99%9C%EC%BD%94%EB%94%A9Node.js-1-12%EA%B0%95</guid>
            <pubDate>Sun, 22 Sep 2024 09:07:36 GMT</pubDate>
            <description><![CDATA[<h2 id="1-수업-소개--2-수업의-목적">1. 수업 소개 ~ 2. 수업의 목적</h2>
<p>Node.js 폭발적 언어. 개쩖. → 이 댕 쩌는 언어를 배워보자!</p>
<h2 id="5-nodejs로-웹서버-만들기">5. Node.js로 웹서버 만들기</h2>
<ul>
<li>웹 서버로 사용 가능</li>
</ul>
<pre><code class="language-jsx">response.end(fs.readFileSync(__dirname + url));</code></pre>
<ul>
<li>response.end() : 사용자에게 전송할 데이터 설정</li>
<li>fs.readFileSync(__dirname + url): 경로 설정된 파일 가져옴</li>
</ul>
<h2 id="68-javascript-문법">6~8. JavaScript 문법</h2>
<ul>
<li><p>Number Data Type</p>
<p>  : 수식 넣으면 계산 결과 출력</p>
<pre><code class="language-jsx">  console.log(1+1); // 2
  console.log(1-1); // 0
  console.log(3*1); // 3
  console.log(4/2); // 2</code></pre>
</li>
<li><p>String</p>
<pre><code class="language-jsx">  console.log(&#39;1&#39;+&#39;1&#39;); // 11
  console.log(&#39;Hello World&#39;.length); // 11 -&gt; 띄어쓰기 포함</code></pre>
</li>
<li><p>Variable</p>
<p>  : 변수 선언</p>
<pre><code class="language-jsx">  var a = 1;
  console.log(a); // 1
  a = 2;
  console.log(a); // 2</code></pre>
<p>  : 변수 활용</p>
<pre><code class="language-jsx">  var greet = &#39;Hello World&#39;;
  console.log(greet); // Hello World

  var letter = greet + &#39;, My name is Hyerin&#39;;
  // Hello World, My name is Hyerin</code></pre>
</li>
<li><p>Template literals</p>
<pre><code class="language-jsx">  var greet = &#39;Hello World&#39;;
  var letter =&#39; ${greet}, My name is Hyerin. ${1+1}&#39;;

  console.log(letter);
  /*
  Hello World, My name is Hyerin. 2

  ${} -&gt; Template literal
  */

  var letter = greet + &#39;\n\n, My name is Hyerin&#39;;

  console.log(letter);
  /*
  Hello World

  ,My name is Hyerin
  */</code></pre>
</li>
</ul>
<h2 id="9-url의-이해">9. URL의 이해</h2>
<p><img src="https://velog.velcdn.com/images/h_rin/post/2855e44d-edef-46a4-8e80-ca418fab5b24/image.png" alt=""></p>
<ul>
<li><p><strong>protocol : 통신규칙</strong>
  → 사용자가 서버에 접속 시 어떤 방식으로 통신할 것인지 나타냄
  → http(Hyper Text Transfer Protocol) : 데이터를 주고 받기 위한 통신 규약</p>
  <br></li>
<li><p>*<em>host(domain) : 호스트 *</em>
  → 인터넷에 접속되어 있는 각각의 컴퓨터를 나타냄</p>
  <br></li>
<li><p>*<em>port : 포트 번호 *</em>
  → 한 대의 컴퓨터 안에 여러대의 서버가 있을 수 있기에 어떤 서버와 통신할지 정함</p>
  <br></li>
<li><p><strong>path : 파일</strong>
  어떤 파일인지 가리킴</p>
  <br></li>
<li><p><strong>query string : 쿼리 스트링</strong>
  → 이 값을 변경하면 웹 서버에 어떤 데이터를 전달 가능</p>
<ul>
<li><strong>?</strong>로 시작하기로 약속</li>
<li><strong>&amp;</strong>으로 값과 값을 구분</li>
<li><strong>=</strong>으로 값의 이름과 값 구분</li>
</ul>
</li>
</ul>
<h2 id="10-url을-통해-입력된-값-사용하기">10. URL을 통해 입력된 값 사용하기</h2>
<p><img src="https://velog.velcdn.com/images/h_rin/post/74022df3-4526-4f85-836d-a24e84c39689/image.png" alt=""></p>
<p>→ 색칠된 부분 = 쿼리 스트링</p>
<pre><code class="language-jsx">var http = require(&#39;http&#39;);
var fs = require(&#39;fs&#39;);
var url = require(&#39;url&#39;); // url이라는 모듈을 사용할 것이다.

var app = http.createServer(function(request,response){
  var _url = request.url;
  var queryData = url.parse(_url, true).query;
  //url 모듈을 통해 받은 객체의 parse 메소드의 parameter로 _url을 넣어 실행
  console.log(queryData); 
  // { id = HTML } 
  console.log(queryData.id);
  // id값 나옴, query string에 따라서 다른 정보 출력 가능

  console.log(_url);
  if(_url == &#39;/&#39;){
    _url = &#39;/index.html&#39;;
  }
  if(_url == &#39;/favicon.ico&#39;){
    return response.writeHead(404);
  }
  response.writeHead(200);
  response.end(queryData.id);
});
app.listen(3000);</code></pre>
<h2 id="11-동적인-웹페이지-만들기">11. 동적인 웹페이지 만들기</h2>
<pre><code class="language-jsx">var http = require(&#39;http&#39;);
var fs = require(&#39;fs&#39;);
var url = require(&#39;url&#39;);

var app = http.createServer(function(request,response){
  var _url = request.url;
  var queryData = url.parse(_url, true).query;
  console.log(queryData.id);
  if(_url == &#39;/&#39;){
    _url = &#39;/index.html&#39;;
  }
  if(_url == &#39;/favicon.ico&#39;){
    return response.writeHead(404);
  }
  response.writeHead(200);
  var template = `
  &lt;!doctype html&gt;
  &lt;html&gt;
  &lt;head&gt;
    &lt;title&gt;WEB1 - HTML&lt;/title&gt;
    &lt;meta charset=&quot;utf-8&quot;&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;h1&gt;&lt;a href=&quot;index.html&quot;&gt;WEB&lt;/a&gt;&lt;/h1&gt;
    &lt;ol&gt;
      &lt;li&gt;&lt;a href=&quot;1.html&quot;&gt;HTML&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;2.html&quot;&gt;CSS&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;3.html&quot;&gt;JavaScript&lt;/a&gt;&lt;/li&gt;
    &lt;/ol&gt;
    &lt;h2&gt;HTML&lt;/h2&gt;
    &lt;p&gt;&lt;a href=&quot;https://www.w3.org/TR/html5/&quot; target=&quot;_blank&quot; title=&quot;html5 speicification&quot;&gt;Hypertext Markup Language (HTML)&lt;/a&gt; is the standard markup language for &lt;strong&gt;creating &lt;u&gt;web&lt;/u&gt; pages&lt;/strong&gt; and web applications.Web browsers receive HTML documents from a web server or from local storage and render them into multimedia web pages. HTML describes the structure of a web page semantically and originally included cues for the appearance of the document.
    &lt;img src=&quot;coding.jpg&quot; width=&quot;100%&quot;&gt;
    &lt;/p&gt;&lt;p style=&quot;margin-top:45px;&quot;&gt;HTML elements are the building blocks of HTML pages. With HTML constructs, images and other objects, such as interactive forms, may be embedded into the rendered page. It provides a means to create structured documents by denoting structural semantics for text such as headings, paragraphs, lists, links, quotes and other items. HTML elements are delineated by tags, written using angle brackets.
    &lt;/p&gt;
  &lt;/body&gt;
  &lt;/html&gt;

  `;
  response.end(queryData.id);
});
app.listen(3000);</code></pre>
<p>위 정적 페이지를 아래 동적 페이지로 바꾼다. </p>
<p>(제목 부분을 동적으로 바꿈)</p>
<pre><code class="language-jsx">var http = require(&#39;http&#39;);
var fs = require(&#39;fs&#39;);
var url = require(&#39;url&#39;);

var app = http.createServer(function(request,response){
  var _url = request.url;
  var queryData = url.parse(_url, true).query;
  var title = queryData.id; &lt;!-- 여기 --&gt;
  console.log(title);
  if(_url == &#39;/&#39;){
    title = &#39;Welcome&#39;;
  }
  if(_url == &#39;/favicon.ico&#39;){
    return response.writeHead(404);
  }
  response.writeHead(200);
  var template = `
  &lt;!doctype html&gt;
  &lt;html&gt;
  &lt;head&gt;
    &lt;title&gt;WEB1 - ${title}&lt;/title&gt; &lt;!-- 여기 --&gt;
    &lt;meta charset=&quot;utf-8&quot;&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;h1&gt;&lt;a href=&quot;/&quot;&gt;WEB&lt;/a&gt;&lt;/h1&gt;
    &lt;ol&gt;
      &lt;li&gt;&lt;a href=&quot;/?id=HTML&quot;&gt;HTML&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;/?id=CSS&quot;&gt;CSS&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;/?id=JavaScript&quot;&gt;JavaScript&lt;/a&gt;&lt;/li&gt;
    &lt;/ol&gt;
    &lt;h2&gt;${title}&lt;/h2&gt; &lt;!-- 여기 --&gt;
    &lt;p&gt;&lt;a href=&quot;https://www.w3.org/TR/html5/&quot; target=&quot;_blank&quot; title=&quot;html5 speicification&quot;&gt;Hypertext Markup Language (HTML)&lt;/a&gt; is the standard markup language for &lt;strong&gt;creating &lt;u&gt;web&lt;/u&gt; pages&lt;/strong&gt; and web applications.Web browsers receive HTML documents from a web server or from local storage and render them into multimedia web pages. HTML describes the structure of a web page semantically and originally included cues for the appearance of the document.
    &lt;img src=&quot;coding.jpg&quot; width=&quot;100%&quot;&gt;
    &lt;/p&gt;&lt;p style=&quot;margin-top:45px;&quot;&gt;HTML elements are the building blocks of HTML pages. With HTML constructs, images and other objects, such as interactive forms, may be embedded into the rendered page. It provides a means to create structured documents by denoting structural semantics for text such as headings, paragraphs, lists, links, quotes and other items. HTML elements are delineated by tags, written using angle brackets.
    &lt;/p&gt;
  &lt;/body&gt;
  &lt;/html&gt;

  `;
  response.end(template); // 여기
});
app.listen(3000);</code></pre>
<h2 id="12-파일-읽기-기능">12. 파일 읽기 기능</h2>
<ul>
<li><p>CRUD : Create, Read, Update, Delete</p>
</li>
<li><p>readFile()</p>
<pre><code class="language-jsx">  var fs = require(&#39;fs&#39;);
  fs.readFile(&#39;sample.txt&#39;, &#39;uft8&#39;,fundtion(err, data){
      console.log(data);
      //fs.readFile( 파일명(파일 경로), 옵션, 콜백함수 )
  });</code></pre>
<p>  → fs 모듈 이용하면 사용 가능. </p>
</li>
</ul>
<p> 끄읕.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Do it! 리액트로 웹앱 만들기 with 타입스크립트] 서평!]]></title>
            <link>https://velog.io/@h_rin/Do-it-%EB%A6%AC%EC%95%A1%ED%8A%B8%EB%A1%9C-%EC%9B%B9%EC%95%B1-%EB%A7%8C%EB%93%A4%EA%B8%B0-with-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%84%9C%ED%8F%89</link>
            <guid>https://velog.io/@h_rin/Do-it-%EB%A6%AC%EC%95%A1%ED%8A%B8%EB%A1%9C-%EC%9B%B9%EC%95%B1-%EB%A7%8C%EB%93%A4%EA%B8%B0-with-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%84%9C%ED%8F%89</guid>
            <pubDate>Tue, 09 Apr 2024 08:17:48 GMT</pubDate>
            <description><![CDATA[<p><a href="https://product.kyobobook.co.kr/detail/S000212754474">Do it! 리액트로 웹앱 만들기 with 타입스크립트
-리액트 + 익스프레스 + 몽고DB로 만드는 SPA와 API 서버-</a></p>
<p><img src="https://velog.velcdn.com/images/h_rin/post/c4477b66-46cb-498e-a730-62b6263c5793/image.jpg" alt=""></p>
<p>학부생 아무것도 모르는 초짜... 리액트 공부를 하고 싶어 교재를 찾던 중 좋은 기회로 서평단에 신청을 하였고 합격했다~!</p>
<p>그래서 써보는 첫 서평!</p>
<blockquote>
<p>리액트 입문서, 독학서로 추천 ⭐⭐🌞⭐⭐
<del>@: 별이 다섯개!!!</del></p>
</blockquote>
<ol>
<li>입문자도 이해하기 쉬운 구성</li>
<li>기본부터 섬세하게!</li>
<li>서버까지~!</li>
</ol>
<p>이상 이 책의 추천이유였다.</p>
<p>매우 간단하지만 나같이 학부생에 처음 시작하는 사람들은 입문 과정에서 무엇을 해야할지, 어디서부터 시작해야 될 지 모르는 경험을 한 사람들이 많을 것이다. </p>
<p>이 책은 이런 사람들에게 길라잡이가 될 것이다. 심지어 이를 입문으로 서버도 공부할 수 있기 때문에 웹앱 프로젝트까지 시도하여 볼 수 있다는 점이 꽤나 매력포인트라고 생각한다. </p>
<p>간단한 서평이였지만 개인적으로 구성이 너무 마음에 들었고 리액트를 공부하고 웹앱을 만들어 보고 싶었던 나에게 아주 도움이 되었다. 만족스럽고 많은 도움이 되는 책이였다. </p>
<p>그런 의미에서 추천
!!!!!╰(<em>°▽°</em>)╯!!!!!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS: 노마드 코더 8강 정리]]></title>
            <link>https://velog.io/@h_rin/JS-%EB%85%B8%EB%A7%88%EB%93%9C-%EC%BD%94%EB%8D%94-8%EA%B0%95-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@h_rin/JS-%EB%85%B8%EB%A7%88%EB%93%9C-%EC%BD%94%EB%8D%94-8%EA%B0%95-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Thu, 18 May 2023 08:05:32 GMT</pubDate>
            <description><![CDATA[<h2 id="80-geolocation">8.0 Geolocation</h2>
<p>weather.js</p>
<pre><code class="language-js">function onGeoOk(position) {
  const lat = position.coords.latitude;
  const lng = position.coords.longitude;
  console.log(&quot;You live in&quot;, lat, lng);
}
function onGeoError() {
  alert(&quot;Can&#39;t find you. No weather for you.&quot;);
}

navigator.geolocation.getCurrentPosition(onGeoOk, onGeoError);</code></pre>
<blockquote>
</blockquote>
<p><code>navigator.geolocation.getCurrentPosition()</code>
:  브라우저에서 위치 좌표를 준다. 위도, 경도, 고도, 장치의 이동방향, 장치의 현재속도, gps, 와이파이 등
** 참고: 사용자의 위치 변화 발생시 호출되는 함수는 watchPosition()</p>
<p>이때 getCurrentPosition 은 2개의 argument가 필요하다. 앞쪽에는 모든 게 잘 됐을 때 실행될 함수인 onGeoOk 함수를, 뒤에는 실패했을 때 실행될 함수인 onGeoError 함수를 입력함</p>
<blockquote>
<p><code>onGeoError() 함수가 실행될 때</code> 
: 에러가 났다는 것을 사용자에게 알려주기 위해서 alert(&quot;Can&#39;t find you. No weather for you.&quot;); 해 줌</p>
</blockquote>
<blockquote>
<p><code>onGeoOk 함수가 실행될 때</code>
: 자바스크립트가 position으로 user의 위치를 전달,
position은 object 이고 위도와 경도 값이 들어있음</p>
</blockquote>
<pre><code class="language-js">function onGeoOk(position){
    const lat = position.coords.latitude;
    const lng = position.coords.longitude;
    console.log(&quot;You live in&quot;, lat, lng);
}</code></pre>
<p>positon.coords.latitude와 position.coords.longitude 를 변수에 저장하고 console.log를 해서 사용자에게 보여준다.</p>
<hr>
<h2 id="81-weather-api">8.1 Weather API</h2>
<p>: 위도와 경도를 장소로 바꿔주는 API 서비스를 사용하고 사용자 위치에 대한 정보를 제공하는 url을 불러온다.</p>
<p>index.html</p>
<pre><code class="language-html">    &lt;div id=&quot;weather&quot;&gt;
      &lt;span&gt;&lt;/span&gt;
      &lt;span&gt;&lt;/span&gt;
    &lt;/div&gt;
    &lt;!--새로 추가, 날씨관련 내용 표시하기 위해--&gt;
    &lt;script src=&quot;js/greetings.js&quot;&gt;&lt;/script&gt;
    &lt;script src=&quot;js/clock.js&quot;&gt;&lt;/script&gt;
    &lt;script src=&quot;js/quotes.js&quot;&gt;&lt;/script&gt;</code></pre>
<p>weather.js</p>
<pre><code class="language-js">const weather = document.querySelector(&quot;#weather span:first-child&quot;);
const city = document.querySelector(&quot;#weather span:last-child&quot;);
const API_KEY = &quot;241051bf13976dd3ddf8b8d9f247255e&quot;;

function onGeoOk(position) {
  const lat = position.coords.latitude;
  const lon = position.coords.longitude;
  const url = `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&amp;lon=${lon}&amp;appid=${API_KEY}&amp;units=metric`;
  fetch(url)
    .then((response) =&gt; response.json())
    .then((data) =&gt; {
      city.innerText = data.name;
      weather.innerText = `${data.weather[0].main} / ${data.main.temp}`;
    });
}
function onGeoError() {
  alert(&quot;Can&#39;t find you. No weather for you.&quot;);
}
navigator.geolocation.getCurrentPosition(onGeoOk, onGeoError);</code></pre>
<pre><code class="language-js">const API_KEY = &quot;1545879515889df25c23857021328cf1&quot;;

function onGeoOK(position) {
  const lat = position.coords.latitude;
  const lon = position.coords.longitude;
  console.log(&quot;You live in&quot;, lat, lon);
  const url = `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&amp;lon=${lon}&amp;appid=${API_KEY}&amp;units=metric`;
  fetch(url)
    .then((response) =&gt; response.json())
    .then((data) =&gt; {
      const weather = document.querySelector(&quot;#weather span:first-child&quot;);
      const city = document.querySelector(&quot;#weather span:last-child&quot;);
      city.innerText = data.name;
      weather.innerText = `${data.weather[0].main} / ${data.main.temp}`;
    }); //js가 url 부름, promise(시간이 좀 걸린 뒤에 일어나는 것)
}

function onGeoError() {
  alert(&quot;Can&#39;t find you. No weather for you.&quot;);
}

navigator.geolocation.getCurrentPosition(onGeoOK, onGeoError);</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS: 노마드 코더 7강 정리]]></title>
            <link>https://velog.io/@h_rin/JS-%EB%85%B8%EB%A7%88%EB%93%9C-%EC%BD%94%EB%8D%94-7%EA%B0%95-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@h_rin/JS-%EB%85%B8%EB%A7%88%EB%93%9C-%EC%BD%94%EB%8D%94-7%EA%B0%95-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Wed, 17 May 2023 20:29:40 GMT</pubDate>
            <description><![CDATA[<h2 id="70-setup">7.0 Setup</h2>
<p>idex.html</p>
<pre><code class="language-html">  &lt;body&gt;
    &lt;h2 id=&quot;clock&quot;&gt;00:00:00&lt;/h2&gt;
    &lt;form class=&quot;hidden&quot; id=&quot;login-form&quot;&gt;
      &lt;input
        required
        maxlength=&quot;15&quot;
        type=&quot;text&quot;
        placeholder=&quot;What is your name?&quot;
      /&gt;
      &lt;input type=&quot;submit&quot; value=&quot;Log In&quot; /&gt;
    &lt;/form&gt;
    &lt;h1 class=&quot;hidden&quot; id=&quot;greeting&quot;&gt;&lt;/h1&gt;
    &lt;!--아래 내용 추가(투두 폼)--&gt;
    &lt;form id=&quot;todo-form&quot;&gt;
      &lt;input type=&quot;text&quot; placeholder=&quot;Write a To Do and Press Enter&quot; required /&gt;
    &lt;/form&gt;
    &lt;ul id=&quot;todo-list&quot;&gt;&lt;/ul&gt;
    &lt;!--ul태그를 만들어서 js로 li태그 추가 예정--&gt;
    &lt;div id=&quot;quote&quot;&gt;
      &lt;span&gt;&lt;/span&gt;
      &lt;span&gt;&lt;/span&gt;
    &lt;/div&gt;
    &lt;script src=&quot;js/greeting.js&quot;&gt;&lt;/script&gt;
    &lt;script src=&quot;js/clock.js&quot;&gt;&lt;/script&gt;
    &lt;script src=&quot;js/quotes.js&quot;&gt;&lt;/script&gt;
    &lt;script src=&quot;js/background.js&quot;&gt;&lt;/script&gt;
  &lt;/body&gt;</code></pre>
<p>todo.js</p>
<pre><code class="language-js">const toDoForm = document.getElementById(&quot;todo-form&quot;);
const toDoInput = toDoForm.querySelector(&quot;#todo-form input&quot;);
//document.querySelector(&quot;#todo-form input&quot;);도 위와 같은 의미
const toDoLlist= document.getElementById(&quot;todo-list&quot;);

function handleToDoSubmit(event) {
    event.preventDefault();
    const newTodo = toDoInput.value; //to-do input의 값을 새로운 변수에 저장하는 것
    toDoInput.value=&quot;&quot;; //todo input의 값을 지워줌
}

toDoForm.addEventListener(&quot;submit&quot;,handleToDoSubmit);</code></pre>
<p><img src="https://velog.velcdn.com/images/h_rin/post/e04a2e80-dc6f-4841-9416-ca2867fcb545/image.png" alt="">
enter 누르고 난 후
<img src="https://velog.velcdn.com/images/h_rin/post/b0644b49-f6fc-49f2-82a0-ee08d92f21f2/image.png" alt=""></p>
<p>=&gt; 계속 새로고침이 되어서 봤더니 index.html에 todo.js를 import해주지 않았음...</p>
<hr>
<h2 id="71-adding-to-do">7.1 Adding To-Do</h2>
<pre><code class="language-js">const toDoForm = document.getElementById(&quot;todo-form&quot;);
const toDoInput = document.querySelector(&quot;#todo-form input&quot;);
const toDoList = document.getElementById(&quot;todo-list&quot;);

// 새로 추가한 함수
function paintToDo(newTodo) {
  const li = document.createElement(&quot;li&quot;); // li태그 추가
  const span = document.createElement(&quot;span&quot;); // span태그 추가
  li.appendChild(span); // li태그 안에 자식을 span 태그로 만듦
  span.innerText = newTodo; // span태그 안에 text가 new Todo 객체가 되도록 함
  toDoList.appendChild(li); // ul 태그 안에 li 를 속하게 함
}

function handleToDoSubmit(event) {
  event.preventDefault();
  const newTodo = toDoInput.value;
  toDoInput.value = &quot;&quot;;
  paintToDo(newTodo); //추가
}

toDoForm.addEventListener(&quot;submit&quot;, handleToDoSubmit);</code></pre>
<blockquote>
<p>li.append(&quot;추가해주세요&quot;) → 가능
li.appendChild(&quot;추가해주세요&quot;) → 불가능 - typeError(객체만 추가 가능)</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/h_rin/post/8439edd2-883a-44cf-b30b-18adb21f6bd3/image.png" alt=""></p>
<p>! 아무것도 입력하지 않았을 경우는 추가되지 않음
-&gt; input에 required를 추가했기 때문</p>
<p>!! 새로고침하면 지워짐 &amp; 목록 지울 수 없음</p>
<hr>
<h2 id="72-deleting-to-do">7.2 Deleting To-Do</h2>
<p>(이모지 단축키 윈도우+.) ✨</p>
<pre><code class="language-js">const toDoForm = document.getElementById(&quot;todo-form&quot;);
const toDoInput = document.querySelector(&quot;#todo-form input&quot;);
const toDoList = document.getElementById(&quot;todo-list&quot;);

function deleteToDo(event) {
    const li = event.target.parentElement; // 이벤트가 발생했을때.해당객체를 지정 후 그것의 부모태그를 지정
    li.remove(); // 위에서 지정된 것을 삭제
}

function paintToDo(newTodo) {
  const li = document.createElement(&quot;li&quot;);
  const span = document.createElement(&quot;span&quot;);
  span.innerText = newTodo;

  const button = document.createElement(&quot;button&quot;); // html에 button 태그를 생성
  button.innerText = &quot;❌&quot;; // 사용자가 버튼을 누르면 텍스트가 삭제되는 기능이 있다는 것을 인식시키기 위해 버튼 내부 텍스트를 &quot;❌&quot;로 정의
  button.addEventListener(&quot;click&quot;, deleteToDo);// 버튼을 Click하면 deleteToDo 함수 실행
  li.appendChild(span);
  li.appendChild(button);
  toDoList.appendChild(li);
}

function handleToDoSubmit(event) {
  event.preventDefault();
  const newTodo = toDoInput.value;
  toDoInput.value = &quot;&quot;;
  paintToDo(newTodo);
}

toDoForm.addEventListener(&quot;submit&quot;, handleToDoSubmit);
</code></pre>
<p><img src="https://velog.velcdn.com/images/h_rin/post/01a0ae42-ebf8-442f-b212-54537894d7fe/image.png" alt="">
x를 누르면 삭제되는 것을 확인할 수 있음
<img src="https://velog.velcdn.com/images/h_rin/post/b86bbbbc-115f-468f-88fe-8222bb6c9f64/image.png" alt=""></p>
<p>&gt;&gt; 하지만 아직도 새로고침하면 내용이 사라진다.</p>
<hr>
<h2 id="73-saving-to-do">7.3 Saving To-Do</h2>
<pre><code class="language-js">const toDoForm = document.getElementById(&quot;todo-form&quot;);
const toDoInput = document.querySelector(&quot;#todo-form input&quot;);
const toDoList = document.getElementById(&quot;todo-list&quot;);

const toDos = [];// toDo에 들어오는 텍스트를 배열로 묶어 보관하기 위해 빈 array를 생성

function saveToDos() {
  localStorage.setItem(&quot;todos&quot;,     JSON.stringify(toDos)); 
  // 값을 string형태의 array로 저장함
  //key: todos value: [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;]
  //로컬 저장소에는 array 저장 불가능, 오직 text만
}

function deleteToDo(event) {
  const li = event.target.parentElement;
  li.remove();
}

function paintToDo(newTodo) {
  const li = document.createElement(&quot;li&quot;);
  const span = document.createElement(&quot;span&quot;);
  span.innerText = newTodo;

  const button = document.createElement(&quot;button&quot;);
  button.innerText = &quot;❌&quot;;
  button.addEventListener(&quot;click&quot;, deleteToDo);
  li.appendChild(span);
  li.appendChild(button);
  toDoList.appendChild(li);
}

function handleToDoSubmit(event) {
  event.preventDefault();
  const newTodo = toDoInput.value;
  toDoInput.value = &quot;&quot;;
  toDos.push(newTodo); 
  //arr1.push(5); -&gt;  [1,2,3,4,5] 
  paintToDo(newTodo);
  saveToDos();
}

toDoForm.addEventListener(&quot;submit&quot;, handleToDoSubmit);</code></pre>
<blockquote>
<p><code>JSON.stringify()</code>
: js object나 array나 어떤 것이든 string data type으로 바꿔주는 기능</p>
</blockquote>
<p>localStorage 에서 확인해봤을 때 값들을 단순한 string 으로 변경하여 <strong>array 모양</strong>으로 저장</p>
<p>ex) JSON.stringify([1,2,3,4]) ---&gt; &quot;[1,2,3,4]&quot; 반환</p>
<hr>
<h2 id="74-5-loading-to-do">7.4-5 Loading To-Do</h2>
<blockquote>
<p><code>JSON.parse()</code>
: string을 array로 바꿈</p>
</blockquote>
<p>ex) JSON.parse(&quot;[1,2,3,4]&quot;) ----&gt; [1,2,3,4] 반환</p>
<p><img src="https://velog.velcdn.com/images/h_rin/post/55066236-b367-4dd6-b5e0-a74707e1b686/image.png" alt=""></p>
<pre><code class="language-js">
const toDoForm = document.getElementById(&quot;todo-form&quot;);
const toDoInput = document.querySelector(&quot;#todo-form input&quot;);
const toDoList = document.getElementById(&quot;todo-list&quot;);

const TODOS_KEY = &quot;todos&quot;; //추가

let toDos = []; //배열의 내용들이 바뀌기 때문에 let으로 변경

function saveToDos() {
  localStorage.setItem(TODOS_KEY, JSON.stringify(toDos));
}
//=== localstorage.setItem(&#39;todos&#39;,&#39;[]&#39;);

function deleteToDo(event) {
  const li = event.target.parentElement;
  li.remove();
}

function paintToDo(newTodo) {
  const li = document.createElement(&quot;li&quot;);
  const span = document.createElement(&quot;span&quot;);
  span.innerText = newTodo;

  const button = document.createElement(&quot;button&quot;);
  button.innerText = &quot;❌&quot;;
  button.addEventListener(&quot;click&quot;, deleteToDo);
  li.appendChild(span);
  li.appendChild(button);
  toDoList.appendChild(li);
}

function handleToDoSubmit(event) {
  event.preventDefault();
  const newTodo = toDoInput.value;
  toDoInput.value = &quot;&quot;;
  toDos.push(newTodo);
  paintToDo(newTodo);
  saveToDos();
}

toDoForm.addEventListener(&quot;submit&quot;, handleToDoSubmit);

const savedToDos = localStorage.getItem(TODOS_KEY);

if (savedToDos !== null) {
  const parsedToDos = JSON.parse(savedToDos);
  toDos = parsedToDos; 
  //전에 있던 toDo들 복원
  //[] = [&#39;a&#39;,&#39;b&#39;,&#39;c&#39;], 시작되면서 빈 값일 경우 로컬저장소에서 가져온 값을 배열로 다시 저장 시킴(불러오기) 
  parsedToDos.forEach((item) =&gt; console.log(&quot;this is the turn of &quot;, item));
  //parsedToDos배열에 들어 있는 각 요소들을 =&gt;실행문 실행시켜줌
}</code></pre>
<blockquote>
</blockquote>
<pre><code class="language-js">(item) =&gt; console.log(&quot;this is the turn of &quot;, item)
--------------------------------------------------
function sayHello(item) {
    console.log(&quot;this is the turn of &quot;, item);
}</code></pre>
<p>위 두 표현은 같은 표현</p>
<p><code>배열이름.forEach((매개변수) =&gt; {실행문})</code>
: 배열이 가지고 있는 모든 값에 대해 같은 함수를 실행시키는 내장함수, array 안의 각각의 요소에 대해 하나의 function을 실행</p>
<hr>
<h2 id="76-8-deleting-to-do">7.6-8 Deleting To-Do</h2>
<p>&gt;&gt; 문제점 &lt;&lt; 
위 코드를 실행 시 불러오고 삭제는 가능하지만 삭제 시 로컬 저장소에 업데이트하지 않음 즉, 리스트를 지우면 화면에서는 지워지지만 새로고침 하면 다시 화면에 나타난다</p>
<p>-&gt; localStaorage에서는 지우지 않았기 때문 localStaorage는 데이터베이스(array)는 단순히 array 값을 복사해두는 곳이므로 값을 지울 때 어떤 값을 지우는지 구분이 안됨</p>
<pre><code class="language-js">const toDoForm = document.getElementById(&quot;todo-form&quot;);
const toDoInput = document.querySelector(&quot;#todo-form input&quot;);
const toDoList = document.getElementById(&quot;todo-list&quot;);

const TODOS_KEY = &quot;todos&quot;;

let toDos = [];

function saveToDos() {
  localStorage.setItem(TODOS_KEY, JSON.stringify(toDos));
}

function deleteToDo(event) {
  const li = event.target.parentElement;
  console.log(li.id); //
  li.remove();
}

function paintToDo(newTodo) {
  const li = document.createElement(&quot;li&quot;);
  li.id = newTodo.id;
  const span = document.createElement(&quot;span&quot;);
  span.innerText = newTodo.text;

  const button = document.createElement(&quot;button&quot;);
  button.innerText = &quot;❌&quot;;
  button.addEventListener(&quot;click&quot;, deleteToDo);
  li.appendChild(span);
  li.appendChild(button);
  toDoList.appendChild(li);
}

function handleToDoSubmit(event) {
  event.preventDefault();
  const newTodo = toDoInput.value;
  toDoInput.value = &quot;&quot;;
  const newTodoObj = {
    text: newTodo,
    id: Date.now(),
  };
  toDos.push(newTodoObj);
  paintToDo(newTodoObj);
  saveToDos();
}

toDoForm.addEventListener(&quot;submit&quot;, handleToDoSubmit);

const savedToDos = localStorage.getItem(TODOS_KEY);

if (savedToDos !== null) {
  const parsedToDos = JSON.parse(savedToDos);
  toDos = parsedToDos;
  parsedToDos.forEach(paintToDo);
}
</code></pre>
<p>&gt;해결법</p>
<blockquote>
</blockquote>
<p>todo에 id를 부여하여 obj를 만들어준다. 
ex) [{id:12415, text=&quot;dfas&quot;}] </p>
<blockquote>
<blockquote>
<p>Date.now()를 사용하여 랜덤으로 id를 만들어줌!
//1970년 1월 1일 0시 0분 0초부터 현재까지 경과된 밀리초를 숫자 값으로 반환하는 내장함수</p>
</blockquote>
</blockquote>
<h3 id="filter">&gt;filter&lt;</h3>
<pre><code class="language-js">const toDoForm = document.getElementById(&quot;todo-form&quot;);
const toDoInput = document.querySelector(&quot;#todo-form input&quot;);
const toDoList = document.getElementById(&quot;todo-list&quot;);

const TODOS_KEY = &quot;todos&quot;;

let toDos = [];

function saveToDos() {
  localStorage.setItem(TODOS_KEY, JSON.stringify(toDos));
  //array 를 저장 console.log [&#39;a&#39;, &#39;b&#39;, &#39;s&#39;] localStorage a,b,c  --&gt; JSON.stringify적용 후  console.log [&#39;f&#39;, &#39;d&#39;, &#39;s&#39;] localStorage[&quot;f&quot;,&quot;d&quot;,&quot;s&quot;]
}

function deleteToDo(event) {
  const li = event.target.parentElement; // 삭제하고 싶은 li
  li.remove(); // li 삭제
  //수정 및 추가
  toDos = toDos.filter((toDo) =&gt; toDo.id !== parseInt(li.id));
  saveToDos();
}

function paintToDo(newTodo) {
  const li = document.createElement(&quot;li&quot;);
  li.id = newTodo.id;

  const span = document.createElement(&quot;span&quot;);
  span.innerText = newTodo.text;

  const button = document.createElement(&quot;button&quot;);
  button.innerText = &quot;❌&quot;;
  button.addEventListener(&quot;click&quot;, deleteToDo);
  li.appendChild(span);
  li.appendChild(button);
  toDoList.appendChild(li);
}

function handleToDoSubmit(event) {
  event.preventDefault();
  const newTodo = toDoInput.value; //인풋의 현재 값을 새로운 변수에 복사하는 것
  toDoInput.value = &quot;&quot;; // 엔터누를때마다 인풋 비어있게 하기. newTodo 변수와 전혀 상관 없음
  const newTodoObj = {
    text: newTodo,
    id: Date.now(),
  };
  toDos.push(newTodoObj); // 배열에 obj 저장
  paintToDo(newTodoObj); // todo 항목 표시하기
  saveToDos();
}

toDoForm.addEventListener(&quot;submit&quot;, handleToDoSubmit);

const savedToDos = localStorage.getItem(TODOS_KEY);

if (savedToDos !== null) {
  const parsedToDos = JSON.parse(savedToDos);
  toDos = parsedToDos;
  parsedToDos.forEach(paintToDo); // // parsedToDos의 각각의 item에 대하여 한개의 함수 실행(화살표함수)
}
</code></pre>
<p><code>배열이름.filter((매개변수) =&gt; {실행문})</code>
: 조건에 해당하는 인자값만 모아놓은 새로운 배열을 반환하는 내장함수</p>
<blockquote>
</blockquote>
<p><strong>function myFilter() {return true}</strong></p>
<pre><code class="language-js">ex)
const arr = [1,2,3,4,5];
function myFilter(item) {return item !== 3};
arr.filter(myFilter)    
// 새로운 배열 [1,2,4,5] 반환 (기존 arr는 그대로) </code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS: 노마드 코더 6강 정리]]></title>
            <link>https://velog.io/@h_rin/JS-%EB%85%B8%EB%A7%88%EB%93%9C-%EC%BD%94%EB%8D%94-6%EA%B0%95-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@h_rin/JS-%EB%85%B8%EB%A7%88%EB%93%9C-%EC%BD%94%EB%8D%94-6%EA%B0%95-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Wed, 17 May 2023 14:26:43 GMT</pubDate>
            <description><![CDATA[<h2 id="60-quotes">6.0 Quotes</h2>
<p><strong>1) Math.random()</strong>
: 0-1까지 사이 아무 숫자 랜덤 출력</p>
<p><strong>2) Math.floor()</strong>
: 소수점 이하를 버림</p>
<p><strong>3) Math.ceil()</strong>
: 소수점 이하를 올림</p>
<p><strong>4) Math.round()</strong>
: 소수점 이하를 반올림</p>
<p>index.html</p>
<pre><code class="language-html">    &lt;h2 id=&quot;clock&quot;&gt;00:00:00&lt;/h2&gt;
    &lt;h1 class=&quot;hidden&quot; id=&quot;greeting&quot;&gt;&lt;/h1&gt;
    &lt;!--내용 추가--&gt;
    &lt;div id=&quot;quote&quot;&gt;
      &lt;span&gt;&lt;/span&gt;
      &lt;span&gt;&lt;/span&gt;
    &lt;/div&gt;
    &lt;!--위 내용 추가함--&gt;
    &lt;script src=&quot;js/greetings.js&quot;&gt;&lt;/script&gt;
    &lt;script src=&quot;js/clock.js&quot;&gt;&lt;/script&gt;
    &lt;script src=&quot;js/quotes.js&quot;&gt;&lt;/script&gt;</code></pre>
<p>quotes.js</p>
<pre><code class="language-js">const quotes = [
    {
      quote: &quot;The way to get started is to quit talking and begin doing.&quot;,
      author: &quot;Walt Disney&quot;,
    },
    {
      quote: &quot;Life is what happens when you&#39;re busy making other plans.&quot;,
      author: &quot;John Lennon&quot;,
    },
    {
      quote:
        &quot;The world is a book and those who do not travel read only one page.&quot;,
      author: &quot;Saint Augustine&quot;,
    },
    {
      quote: &quot;Life is either a daring adventure or nothing at all.&quot;,
      author: &quot;Helen Keller&quot;,
    },
    {
      quote: &quot;To Travel is to Live&quot;,
      author: &quot;Hans Christian Andersen&quot;,
    },
    {
      quote: &quot;Only a life lived for others is a life worthwhile.&quot;,
      author: &quot;Albert Einstein&quot;,
    },
    {
      quote: &quot;You only live once, but if you do it right, once is enough.&quot;,
      author: &quot;Mae West&quot;,
    },
    {
      quote: &quot;Never go on trips with anyone you do not love.&quot;,
      author: &quot;Hemmingway&quot;,
    },
    {
      quote: &quot;We wander for distraction, but we travel for fulfilment.&quot;,
      author: &quot;Hilaire Belloc&quot;,
    },
    {
      quote: &quot;Travel expands the mind and fills the gap.&quot;,
      author: &quot;Sheda Savage&quot;,
    },
  ];

const quote = document.querySelector(&quot;#quote span:first-child&quot;); //첫번째 span태그
const author = document.querySelector(&quot;#quote span:last-child&quot;); //두번째 span태그
const todaysQuote = quotes[Math.floor(Math.random() * quotes.length)];
//랜덤함수의 값에서 *10을 하면 1부터 10사이의 값을 구할 수 있음 -&gt; 3.55534554, 6.3453345
//소수점 이하를 없애기 위해 Math.floor사용
//quotes.length -&gt; quotes배열의 길이 나타냄

quote.innerText = todaysQuote.quote;
author.innerText = todaysQuote.author;</code></pre>
<blockquote>
<p>배열.length == 배열의 길이</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/h_rin/post/d11f0410-d9f6-47df-9cac-bd58c81770a1/image.png" alt=""></p>
<p>새로고침 후
<img src="https://velog.velcdn.com/images/h_rin/post/e9d2e8f9-2f31-4da9-977f-fe2811409c4a/image.png" alt=""></p>
<hr>
<h2 id="61-background">6.1 Background</h2>
<p><strong>1) document.createElement(&quot;img&quot;)</strong>
: <code>createElement</code> element(요소) 생성</p>
<p><strong>2) document.body.appendChild()</strong>
: <code>appendChild</code> 선택한 요소에 자식요소 추가</p>
<br>

<p>js/background.js</p>
<pre><code class="language-js">const images = [&quot;0.jpg&quot;, &quot;1.jpg&quot;, &quot;2.jpg&quot;, &quot;3.jpg&quot;, &quot;4.jpg&quot;];

const chosenImage = images[Math.floor(Math.random() * images.length)];

const bgImage = document.createElement(&quot;img&quot;); //이미지 태그 추가

bgImage.src = `img/${chosenImage}`; //이미지태그에 경로 설정

document.body.appendChild(bgImage);
//&lt;img src=&quot;img/0.jpg&quot;&gt; 이런식으로 생성됨</code></pre>
<p><img src="https://velog.velcdn.com/images/h_rin/post/8cc2d03e-720e-44b7-98e2-7ec6a4bd812f/image.png" alt=""></p>
<hr>
<h3 id="-js에서-html-요소를-생성">+ JS에서 html 요소를 생성</h3>
<p>createElement(&quot; &quot;)
ex) document.createElement(&quot;img&quot;)일 경우 html 내에 img 태그를 생성</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS: 노마드 코더 5강 정리]]></title>
            <link>https://velog.io/@h_rin/JS-%EB%85%B8%EB%A7%88%EB%93%9C-%EC%BD%94%EB%8D%94-5%EA%B0%95-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@h_rin/JS-%EB%85%B8%EB%A7%88%EB%93%9C-%EC%BD%94%EB%8D%94-5%EA%B0%95-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Tue, 16 May 2023 18:46:57 GMT</pubDate>
            <description><![CDATA[<h2 id="50-intervals">5.0 Intervals</h2>
<p>~ 각 파일로 세분화 시켜서 코드를 저장하자 ~</p>
<p>index.html</p>
<pre><code class="language-html">    &lt;h2 id=&quot;clock&quot;&gt;00:00&lt;/h2&gt;
    &lt;!--위 내용을 추가함--&gt;
    &lt;h1 class=&quot;hidden&quot; id=&quot;greeting&quot;&gt;&lt;/h1&gt;
    &lt;script src=&quot;app.js&quot;&gt;&lt;/script&gt;
    &lt;script src=&quot;js/greetings.js&quot;&gt;&lt;/script&gt;
    &lt;script src=&quot;js/clock.js&quot;&gt;&lt;/script&gt;
  &lt;/body&gt;</code></pre>
<p>js/clock.js</p>
<pre><code class="language-js">const clock = document.querySelector(&quot;h2#clock&quot;);
//h2태그 안에 있는 id값으로 연결

function sayHello() {
  console.log(&quot;hello&quot;);
}

setInterval(sayHello, 5000); // 5초 마다 sayHello 함수 불러오기</code></pre>
<blockquote>
<p>interval = 매번 일어나야 하는 무언가(간격)
&gt;&gt; setInterval(실행할 함수, 실행할 함수의 주기);</p>
</blockquote>
<ul>
<li>후손 셀렉터(Descendent Selector) : &#39;스페이스&#39;로 연결</li>
<li>자식 셀렉터(Child Selector) : &#39; &gt;&#39; 로 연결
→ (&quot;h2 #id&quot;)로 하면 h2태그 안에 있는 id에 접근</li>
</ul>
<p>→ (&quot;h2#id&quot;) : h2태그 중 해당 ID를 가리킴</p>
<hr>
<h2 id="51-timeouts-and-dates">5.1 Timeouts and Dates</h2>
<h3 id="timeoust--interval">&gt;Timeoust &amp; Interval&lt;</h3>
<p><strong>1) setInterval(실행함수, 주기)</strong>
: 함수를 주기(millisecond)마다 반복함</p>
<p><strong>2) setTimeout(실행함수, 주기)</strong>
: 함수를 주기(millisecond)만큼 기다렸다 한 번만 실행</p>
<h3 id="date">&gt;Date&lt;</h3>
<p>: Date객체는 브라우저 안에 내장되어 있는 객체
→ <code>const date = new Date();</code> 로 미리 선언하면 <code>date.----();</code>형식으로 사용가능</p>
<p><strong>1) new Date().getHours()</strong>
: 시간 정보를 가져옴</p>
<p><strong>2) new Date().getMinutes()</strong>
: 분 정보를 가져옴</p>
<p><strong>3) new Date().getSeconds()</strong>
: 초 정보를 가져옴
<br></p>
<p>clock.js</p>
<pre><code class="language-js">const clock = document.querySelector(&quot;h2#clock&quot;);

function getClock() {
    const date = new Date();
    clock.innerText = `${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`;
}

getClock();
setInterval(getClock, 1000);</code></pre>
<p>&gt;&gt; 숫자가 이런 식으로 <strong><em>안 예뻐서</em></strong> 수정해야 함. </p>
<blockquote>
<p><img src="https://velog.velcdn.com/images/h_rin/post/a8576a8d-15db-4300-a28b-2d1cb1b5ed67/image.png" alt=""></p>
</blockquote>
<hr>
<h2 id="52-padstart">5.2 PadStart</h2>
<p><strong>1) padStart(length, 길이만큼 채울 내용) / padEnd(length, 길이만큼 채울 내용)</strong>
: length는 원하는 string의 길이이고 ,뒤에 원하는 길이가 되기 위해 채울 내용을 정함</p>
<p>→ Start면 앞부분부터 채워지고 End는 뒷 부분부터 채워짐</p>
<p><strong>2) String()</strong>
: number를 string으로 바꾸는 방법은 String() 안에 감싸면 됨</p>
<p>clock.js</p>
<pre><code class="language-js">const clock = document.querySelector(&quot;h2#clock&quot;);

function getClock() {
    const date = new Date();
    const hours = String(date.getHours()).padStart(2, &quot;0&quot;);
    const minutes = String(date.getMinutes()).padStart(2, &quot;0&quot;);
    const seconds = String(date.getSeconds()).padStart(2, &quot;0&quot;);
    clock.innerText = `${hours}:${minutes}:${seconds}`;
}

getClock();
setInterval(getClock, 1000);</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/h_rin/post/c6fdbb7b-2514-47c4-9dd2-9d0c694fc91b/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS: 노마드코더 4강 정리]]></title>
            <link>https://velog.io/@h_rin/JS-%EB%85%B8%EB%A7%88%EB%93%9C%EC%BD%94%EB%8D%94-4%EA%B0%95-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@h_rin/JS-%EB%85%B8%EB%A7%88%EB%93%9C%EC%BD%94%EB%8D%94-4%EA%B0%95-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Sun, 14 May 2023 16:05:50 GMT</pubDate>
            <description><![CDATA[<h2 id="40-input-values">4.0 Input Values</h2>
<p>index.html</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot; /&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&gt;
    &lt;link rel=&quot;stylesheet&quot; href=&quot;style.css&quot; /&gt;
    &lt;title&gt;Momentum App&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;div id=&quot;login-form&quot;&gt;
    &lt;!-- div태그로 묶고 id 생성, 원하는 태그 찾기위해 --&gt;
      &lt;input type=&quot;text&quot; placeholder=&quot;What is your name?&quot; /&gt; 
      &lt;!-- 사용자는 텍스트를 입력, type=&#39;text&#39;로 지정해 줌 --&gt;
      &lt;!-- input박스 안에 미리보기로 작성할 말은 placeholder=&quot;&quot;로 지정 --&gt;
      &lt;button&gt;Log In&lt;/button&gt;
    &lt;/div&gt;
    &lt;script src=&quot;app.js&quot;&gt;&lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<br>

<p>app.js</p>
<pre><code class="language-js">const loginInput = document.querySelector(&quot;#login-form input&quot;);
const loginButton = document.querySelector(&quot;#login-form button&quot;);
//querySelector 로 id태그를 쓸때는 #를 써야함
//getElementById 로 id태그를 쓸땐 #를 안써도 OK
// console.dir() 불러온 함수의 내부(element)를 세부적으로 보여줌

function onLoginBtnClick() {
  console.log(&quot;hello&quot;, loginInput.value);
}

loginButton.addEventListener(&quot;click&quot;, onLoginBtnClick);</code></pre>
<h2 id="41-form-submission">4.1 Form Submission</h2>
<blockquote>
<p>4.0과 다르게 input에 추가적으로 속성을 지정해줌
→ required maxlength=&quot;15&quot;</p>
</blockquote>
<p>index.html</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
    &lt;link rel=&quot;stylesheet&quot; href=&quot;style.css&quot;&gt;&lt;/link&gt; 
    &lt;title&gt;Moment App&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;script src=&quot;app.js&quot;&gt;&lt;/script&gt;
    &lt;form id=&quot;login-form&quot;&gt;
        &lt;input required maxlength=&quot;15&quot; type=&quot;text&quot;
          placeholder=&quot;What is your name?&quot;
        /&gt;
      &lt;!-- input에 입력하는 text의 길이를 15로 제한함 --&gt;
        &lt;input type=&quot;submit&quot; value=&quot;Log In&quot; /&gt;
      &lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>→ 이 방법은 browser에서 
<br></p>
<hr>
<p><strong>&gt;&gt;if&lt;&lt;</strong> 
위 html에서 maxlength가 없다면, js코드에서 조건 설정을 해줘야 함, 근데 이 방식 별로임;
<em>_<del>절대 user를 믿지마. 절대 믿으면 안 돼</del></em></p>
<p>app.js</p>
<pre><code class="language-js">const loginInput = document.querySelector(&quot;#login-form input&quot;);
const loginButton = document.querySelector(&quot;#login-form button&quot;);

function onLoginBtnClick() {
    console.log(&quot;hello&quot;, loginInput.value);
    const username = loginInput.value;

    if (username === &quot;&quot;) {
      alert(&quot;제발 니 이름써&quot;);
    } else if (username.length &gt; 15) {
      alert(&quot;니 이름 너무 길어.&quot;);
    }
}
  loginButton.addEventListener(&quot;click&quot;, onLoginBtnClick);</code></pre>
<h2 id="42-3-events">4.2-3 Events</h2>
<p><strong>1) part 1</strong></p>
<pre><code class="language-js">function onLoginSubmit(event){
    event.preventDefault(); 
      // 브라우저가 기본 동작을 실행하지 못하게 막기 
      // event object는 preventDefault함수를 기본적으로 갖고 있음
    console.log(event);
}

loginForm.addEventListener(&quot;submit&quot;, onLoginSubmit); 
// submit 이벤트가 발생한다면, onLoginSubmit함수를 실행시킨다는 의미 
// JS는 onLoginSubmit함수 호출시 인자를 담아서 호출함. 해당 인자는 event object를 담은 정보들</code></pre>
<ul>
<li>form submit시 브라우저는 기본적으로 새로고침함
→ preventDefault() 함수 추가하여 브라우저의 기본 동작을 막을 수 있음</li>
<li>preventDefault 함수는 EventListener 함수의 &#39;첫 번째 argument&#39; 안에 있는 함수 → 첫 arument는 지금 막 벌어진 event들에 대한 정보를 갖음</li>
</ul>
<blockquote>
<p>JS는(기본적으로)argument를 담아서 함수를 호출하는데, 이 argument가 기본 정보들을 제공하고 있음</p>
</blockquote>
<p><strong>2) part 2</strong></p>
<ul>
<li>addEventListener 안에 있는 함수는 직접 실행하지 않음 
→ 브라우저가 실행시킴, 브라우저에서 해당 이벤트에 대한 정보 즉, object를 가지게 됨 </li>
<li>addEventListener의 함수에서 object에 대한 자리만 할당해주면 해당 이벤트가 발생시킨 정보들에 대한 object들을 볼 수 있음 
→ 해당 이벤트가 가진 기본 Default값을 발생시키지 않기 하게 위해선 preventDefault를 이용하여 막을 수 있음</li>
</ul>
<h2 id="44-getting-username">4.4 Getting Username</h2>
<h4 id="1-hidden">1) .hidden</h4>
<p>style.css</p>
<pre><code class="language-js">.hidden{
  display: none
}</code></pre>
<p>app.js</p>
<pre><code class="language-js">function onLoginSubmit(event) {
  event.preventDefault(); //자동 새로고침 방지
  const username = loginInput.value; 
  loginForm.classList.add(&quot;hidden&quot;); //로그인 폼 숨김
  console.log(username);
}

loginForm.addEventListener(&quot;submit&quot;, onLoginSubmit);</code></pre>
<p>&gt;&gt; 사용자에게 이름을 입력받고 새로고침되지 않은 채로 로그임 폼을 숨김</p>
<p>* visibility:hidden은 공간은 그대로 두고 보이지만 않음  / display:none은 잡아둔 공간도 사라짐 </p>
<h4 id="2-remove">2) remove</h4>
<p>index.html</p>
<pre><code class="language-html">  &lt;body&gt;
    &lt;form id=&quot;login-form&quot;&gt;
      &lt;input
        required
        maxlength=&quot;15&quot;
        type=&quot;text&quot;
        placeholder=&quot;What is your name?&quot;
      /&gt;
      &lt;input type=&quot;submit&quot; value=&quot;Log In&quot; /&gt;
    &lt;/form&gt;
    &lt;h1 id=&quot;greeting&quot; class=&quot;hidden&quot;&gt;&lt;/h1&gt; 
    &lt;!-- 추가한 부분^ --&gt;
    &lt;script src=&quot;app.js&quot;&gt;&lt;/script&gt;
  &lt;/body&gt;</code></pre>
<p>app.js</p>
<pre><code class="language-js">const loginForm = document.querySelector(&quot;#login-form&quot;);
const loginInput = document.querySelector(&quot;#login-form input&quot;);
const loginButton = document.querySelector(&quot;#login-form button&quot;);
const greeting = document.querySelector(&quot;#greeting&quot;);


const HIDDEN_CLASSNAME = &quot;hidden&quot;; //&quot;hidden&quot;을 두 개 이상 사용하기 때문에

function onLoginSubmit(event) {
  event.preventDefault();
  loginForm.classList.add(HIDDEN_CLASSNAME);
  const username = loginInput.value;

  greeting.innerText = &quot;Hello &quot; + username; //h1태그로 표시될 내용
  greeting.classList.remove(HIDDEN_CLASSNAME);//h1태그에서 hidden class를 제거함
}

loginForm.addEventListener(&quot;submit&quot;, onLoginSubmit);</code></pre>
<p>&gt;&gt; greeting.classList.remove(HIDDEN_CLASSNAME); 코드를 통해 hidden 됐던 문구가 나타남</p>
<blockquote>
<p>&quot;Hello &quot; + username; ===<code>Hello ${username}</code>; 
되도록 이면 후자의 방법을 사용</p>
</blockquote>
<blockquote>
<p>const HIDDEN_CLASSNAME = &#39;hidden&#39;;
관습: string만 포함된 변수는 대문자로 쓴다 + 중요한 변수가 아니기 때문에</p>
</blockquote>
<h2 id="45-saving-username">4.5 Saving Username</h2>
<p><strong>1) localStorage.setItem(key, value)</strong>
: 로컬 저장소에 해당 키와 값을 저장함</p>
<p><strong>2) localStorage.getItem(key)</strong>
: 로컬 저장소에 해당 키에 해당되는 값을 불러옴 </p>
<p><strong>3) localStorage.removeItem(key)</strong>
: 로컬 저장소에 해당 키에 해당되는 키, 값을 삭제함</p>
<p>app.js</p>
<pre><code class="language-js">const loginForm = document.querySelector(&quot;#login-form&quot;);
const loginInput = document.querySelector(&quot;#login-form input&quot;);
const greeting = document.querySelector(&quot;#greeting&quot;);
const HIDDEN_CLASSNAME = &quot;hidden&quot;;

function onLoginSubmit(event) {
  event.preventDefault();
  loginForm.classList.add(HIDDEN_CLASSNAME);
  const username = loginInput.value;

  localStorage.setItem(&quot;username&quot;, username);
  // 로컬 저장소에서 &quot;username&quot;이라는 키값과 값을 저장함
  greeting.innerText = `Hello ${username}`;
  greeting.classList.remove(HIDDEN_CLASSNAME);
}

loginForm.addEventListener(&quot;submit&quot;, onLoginSubmit);</code></pre>
<h2 id="46-loading-username">4.6 Loading Username</h2>
<p>index.html</p>
<pre><code class="language-html">&lt;body&gt;
    &lt;form class=&quot;hidden&quot; id=&quot;login-form&quot;&gt;
    &lt;!-- class hidden 추가함 --&gt;
      &lt;input
        required
        maxlength=&quot;15&quot;
        type=&quot;text&quot;
        placeholder=&quot;What is your name?&quot;
      /&gt;
      &lt;input type=&quot;submit&quot; value=&quot;Log In&quot; /&gt;
    &lt;/form&gt;
    &lt;h1 id=&quot;greeting&quot; class=&quot;hidden&quot;&gt;&lt;/h1&gt;
    &lt;script src=&quot;app.js&quot;&gt;&lt;/script&gt;
  &lt;/body&gt;</code></pre>
<p>app.js</p>
<pre><code class="language-js">const greeting = document.querySelector(&quot;#greeting&quot;);

const HIDDEN_CLASSNAME = &quot;hidden&quot;;
const USERNAME_KEY = &quot;username&quot;;

function onLoginSubmit(event) {
  event.preventDefault();
  loginForm.classList.add(HIDDEN_CLASSNAME);
  const username = loginInput.value;

  localStorage.setItem(USERNAME_KEY, username);
  paintGreetings(username);
}

function paintGreetings(username) {  
  greeting.innerText = `Hello ${username}`;
  // username을 받고 greeting 아이디를 갖고 있는 태그 안에 Hello username이 뜨게 만듦
  greeting.classList.remove(HIDDEN_CLASSNAME);
  // greeting 아이디를 갖는 태그에 있는 class 값을 제거함
}

const savedUsername = localStorage.getItem(USERNAME_KEY); 
// 저장되어 있는 username이라는 키에 들어있는 값을 불러옴

if (savedUsername === null) { // 저장된 username값이 없을 때 
  loginForm.classList.remove(HIDDEN_CLASSNAME);
  loginForm.addEventListener(&quot;submit&quot;, onLoginSubmit);
} else {
  paintGreetings(savedUsername);
}</code></pre>
<h3 id="완성본">&lt;완성본&gt;</h3>
<ol>
<li>이름을 적을 수 있는 input form이 나온다.
<img src="https://velog.velcdn.com/images/h_rin/post/c2cc32c8-26f2-4542-bb82-b2e13e7c89be/image.png" alt=""></li>
<li>이름을 입력하면 <input> <input type="submit" value="Log In" /> 이 사라지고 이름과 함께 Hello가 붙어서 화면에 출력된다. 
<img src="https://velog.velcdn.com/images/h_rin/post/31ce17b9-3f4f-4c73-ae15-e5f2fa7ef544/image.png" alt="">
로컬 저장소에 저장된 유저이름을 볼 수 있다. 
<img src="https://velog.velcdn.com/images/h_rin/post/bb7abee1-a420-44ae-acfd-c2deaf32e145/image.png" alt=""></li>
</ol>
]]></description>
        </item>
    </channel>
</rss>