<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>kyeonghun_kim.log</title>
        <link>https://velog.io/</link>
        <description>기본에 충실하자!</description>
        <lastBuildDate>Tue, 28 May 2024 08:15:20 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>kyeonghun_kim.log</title>
            <url>https://velog.velcdn.com/images/kyeonghun_kim/profile/ccef2710-d470-432e-8425-7763442bdc42/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. kyeonghun_kim.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/kyeonghun_kim" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[리뷰]Deep Neural Networks for YouTube Recommendations]]></title>
            <link>https://velog.io/@kyeonghun_kim/%EB%A6%AC%EB%B7%B0Deep-Neural-Networks-for-YouTube-Recommendations</link>
            <guid>https://velog.io/@kyeonghun_kim/%EB%A6%AC%EB%B7%B0Deep-Neural-Networks-for-YouTube-Recommendations</guid>
            <pubDate>Tue, 28 May 2024 08:15:20 GMT</pubDate>
            <description><![CDATA[<h4 id="deep-neural-networks-for-youtube-recommendations"><a href="https://static.googleusercontent.com/media/research.google.com/ko//pubs/archive/45530.pdf">Deep Neural Networks for YouTube Recommendations</a></h4>
<hr>
<h2 id="0-abstract">0. ABSTRACT</h2>
<p>&ensp;YouTube는 현존하는 가장 정교하고 거대한 추천시스템을 가지고 있으며 이 논문에서는 YouTube의 수준 높은 시스템과 Deep Learning의 도입이 가져온 드라마틱한 성능 향상을 설명한다.</p>
<p>&ensp;이 논문은 Information Retrieval에서 기본적인 구조인 Candidate Generation Model(후보생성 모델)과 Ranking Model(랭킹 모델)을 설명할 것이다. 또한 YouTube가 추천 시스템을 설계, 유지하는 과정에서 인사이트를 공유할 예정이다.</p>
<hr>
<h2 id="1-introduction">1. INTRODUCTION</h2>
<p>&ensp;YouTube는 전 세계 최대 규모의 영상 플랫폼으로 YouTube의 추천 시스템은 10억 명 이상의 User들이 개인화된 Content를 찾는 데 도움을 주고 있다. 이 논문에서는 2010년과 마찬가지로 3가지 도전 과제에 대해 집중할 것이다.</p>
<ul>
<li><p><strong>Scale</strong>
YouTube의 거대한 User, Video data를 다루기 위해서는 고도의  distributed learning 알고리즘과 효율적인 Serving 시스템이 필요함.</p>
</li>
<li><p><strong>Freshness</strong>
YouTube는 초 단위로 Video가 업로드 되는 매우 동적인 Data를 가지고 있음. 그렇기 때문에 추천 시스템은 새로 업로드 된 Content 뿐만 아니라 사용자가 최근에 취한 Action도 모델링 할 수 있을 만큼 반응성이 뛰어나야 함.</p>
</li>
<li><p><strong>Noise</strong>
YouTube User의 행동은 Sparsity와 관찰할 수 없는 외부 요인들 때문에 예측하기가 매우 어렵다는 특성이 있음. YouTube는 User의 선호에 대한 Ground-Truth를 얻지 못하고 대신에 implicit feedback signal를 사용하여 Modeling을 진행함.</p>
</li>
</ul>
<h2 id="2-system-overview">2. SYSTEM OVERVIEW</h2>
<p><img src="https://velog.velcdn.com/images/kyeonghun_kim/post/3890ae41-c4f8-4a1d-92b5-bc9b399637cb/image.png" alt=""></p>
<p>&ensp;YouTube 추천 시스템의 구조는 위의 그림과 같이 candidate generation, ranking 두 부분으로 구성된다.</p>
<p>&ensp;<strong>Candidate Generation Network</strong>는 YouTube User의 행동 데이터를 Input으로 받고 Large Corpus에서 후보 비디오를 검색한다. 이러한 후보 비디오들은 User들의 선호도를 높은 정밀도로 예측하도록 설계됐다.</p>
<p>&ensp;또한 Candidates를 생성할 때는 협업 필터링을 사용하여 간단한 개인화를 제공하며 User 간의 유사성은 비디오 시청 ID, 검색 쿼리 토큰, 인구 통계 등과 같은 기본적인 특성을 통해 표현된다.</p>
<p>&ensp;가장 좋은 추천을 제시하기 위해서는 각 후보 비디오들의 중요성을 정밀하게 구별할 수 있어야 한다. 이를 위해 <strong>Ranking Network</strong>가 사용된다. Ranking Network는 User, Video와 관련된 특성들을 사용하여 각각의 Video에 점수를 매기고 점수가 높은 정도에 따라 User들에게 순서대로 표시된다.</p>
<p>&ensp;두 단계의 모델은 수백만개의 비디오 중에서 User가 선호하는 개인화 Video들을 추천할 수 있도록 해준다. 게다가 이러한 설계는 다른 출처에서 생성된 후보들도 포함할 수 있도록 해준다.</p>
<h2 id="3-candidate-generation">3. CANDIDATE GENERATION</h2>
<p>&ensp;수백만 개의 Video 중 일정한 기준에 따라 수백개의 후보들을 생성한다. YouTube의 이전 추천 시스템은 Matrix Factorization 방식을 채택했다. 이 논문에서 제시하는 방식도 이러한 Matrix Factorization 방식을 모방한 것이며 Factorization의 비선형화 방식이라고 할 수 있다.</p>
<h3 id="31-recommendation-as-classification">3.1 Recommendation as Classification</h3>
<p>$$P(wt = i \mid U, C) = \frac{e^{v_{i}^{u}}}{\sum_{j \in V} e^{v_{j}^{u}}}$$</p>
<p>&ensp;YouTube는 추천의 문제를 <strong>Extreme Multiclass Classificaion</strong>으로 재정의 했다. 이 문제는 특정 시간(t)에 User(U)가 Context(C)를 가지고 있을 때 수백만 개의 비디오(V) 중에서 각각의 비디오(i)를 볼 확률(wt)을 구하는 것이다. 수식을 살펴보면 softmax 함수인 것을 확인할 수 있다.</p>
<p>&ensp;$$u∈ℝ^N$$은 User와 Context 쌍에 대한 고차원 Embedding을 나타내고 $$v_j∈ℝ^N$$는 각각의 후보 비디오에 대한 Embedding을 나타낸다. Embedding은 단순히 sparse entities를 맵핑한 것이며 이러한 User Embedding을 학습하여 Video 구별 성능을 향상시킨다.</p>
<p>&ensp;YouTube는 &#39;좋아요&#39;, &#39;싫어요&#39;와 같은 explicit feedback이 존재하지만 이 데이터 보다는 implicit feedback을 사용했고 영상을 끝까지 본 것을 Positive Example로 설정했다. 그 이유는 implicit feedback이 explicit feedback 보다 훨씬 많고 explicit feedback이 부족한 영상들에 대해서도 추천을 생성할 수 있기 때문이다.</p>
<p><strong>Efficient Extreme Multiclass</strong></p>
<p>&ensp;YouTube는 수백만 개의 class를 가지고 있는 Model을 효율적으로 학습시키기 위해서 Negative Sampling을 사용했고 이러한 방식은 속도와 성능을 향상시켰다.</p>
<p>&ensp;Model의 성능을 고려하면서도 Serving latency를 고려해야 한다. 이전의 YouTube 시스템은 해싱 알고리즘을 사용했으며 이 논문에서도 비슷한 방식을 채택했다. 이 model에는 Nearest Neighbor Search 방식을 채택했고 A/B Test에서도 이 방식을 선택하는 것이 민감하지 않다는 것을 확인했다.</p>
<h3 id="32-model-architecture">3.2 Model Architecture</h3>
<p><img src="https://velog.velcdn.com/images/kyeonghun_kim/post/222dcb6f-ff8e-4411-a224-9626b78615f7/image.png" alt=""></p>
<p>&ensp;YouTube는 word2vec의 CBOW 방식을 참고하여 각각의 video에 대한 video ID를 고차원 embedding한다. </p>
<p>&ensp;User의 시청 이력은 가변 크기를 가지고 있기 때문에 Embedding을 통해 dense vector로 변환된다. model은 고정된 크기의 dense inputs가 필요하며 vector들을 단순 평균 내는 방식이 가장 성능이 좋았다.</p>
<h3 id="33-heterogeneous-signals">3.3 Heterogeneous Signals</h3>
<p>&ensp;일반화된 matrix factorization를 사용할 수 있는 DL의 가장 큰 장점은 Feature들을 쉽게 모델에 추가할 수 있다는 것이다.</p>
<p>&ensp;검색 기록은 시청 이력과 비슷하게 각각의 query를 token화 하고 token들은 Embedding 된다. 또한 이러한 vector들은 모두 평균을 내서 사용된다.</p>
<p>&ensp;Demo features는 새로운 user에게 추천을 제공하는 데 중요한 feature이며 Gender feature는 단순하게 0과 1 두 값으로 모델에 입력된다.</p>
<p><strong>“Example Age” Feature</strong>
&ensp;YouTube는 매 초 수많은 Video들이 업로드 되고 있다. User들은 최근 업로드 된 video들을 선호하는 경향이 있기 때문에 이 video들을 추천해야 한다. 그러나 과거의 데이터를 활용하여 미래를 예측하기 때문에 과거의 아이템에 편향된 결과를 제공하는 문제가 있다.</p>
<p>&ensp;또한, 인기 있는 비디오는 시간에 따라 매우 크게 변화하고 이러한 변화를 반영하기 위해 YouTube의 추천 시스템은 지난 몇 주 간의 평균 시청 기록을 반영하여 추천을 제공한다. 이 때 평균 시청 기록은 시간성을 보장하지 못한다는 단점이 있다.</p>
<p>&ensp;이것들을 보정하기 위해서 Example Age라는 Feature를 학습에 사용하고 있으며 이것은 성능을 향상시키는 결과를 가져왔다. 실제 예측 시에는 이 Feature를 0 또는 음수 값으로 설정하는데 이것은 모델이 최신 데이터를 기반으로 예측할 수 있도록 하기 위함이다.</p>
<h3 id="34-label-and-context-selection">3.4 Label and Context Selection</h3>
<p>&ensp;Training Example으로 외부 사이트 출처의 시청 기록을 포함한 YouTube의 모든 시청 기록을 사용한다. 그렇게 하지 않으면 새로운 Content가 노출되기 어렵고 기존의 인기 Content에 편향된 결과를 얻을 수 있기 때문이다.</p>
<p>&ensp;User들이 추천 Content가 아닌 Video를 발견하면 CF를 통해 다른 User들에게 빠르게 이 Content가 전파되는 방식으로 설계했으며 User당 고정된 수의 Training Example을 할당하여 모든 User에게 동일한 Weight를 주도록 설계했다. 특히 User의 Weight를 고정시킨 것은 헤비 유저의 bias와 추천을 제한하기 위해서이다.</p>
<p>&ensp;User의 시청 패턴은 불규칙적이기 때문에 이러한 특성을 반영할 수 있는 시스템을 설계해야 한다. 에피소드 형태의 Content는 순차적으로 시청하는 경향이 있고 특정 장르에 대한 관심이 생겼을 경우에는 가장 인기 있는 Content부터 Niche Content로 시청하는 경향이 있다. 따라서 어떤 User가 &quot;Talyer Swift&quot;를 검색했을 때, 단순히 &quot;Talyer Swift&quot;와 관련된 Content만을 추천해주는 것은 이러한 불규칙적 패턴을 반영하지 못하는 시스템이 될 것이다.</p>
<p>&ensp;YouTube에서는 이러한 User의 불규칙적 시청 패턴을 반영하기 위해서 실제 label 지점을 기준으로 이전의 데이터만을 사용해 모델을 학습시켰고 이것은 매우 좋은 성능을 보여줬다.</p>
<p><img src="https://velog.velcdn.com/images/kyeonghun_kim/post/a143d8bf-cc9b-4b85-9ebe-619d2853672e/image.png" alt=""></p>
<h2 id="4-ranking">4. RANKING</h2>
<p>&ensp;어떤 User가 시청할 확률이 높은 Video를 추천 받았지만 thumnail image 때문에 클릭을 하지 않을 수 있다. 이때 Ranking 시스템의 역할은 Candidate Generation을 보정하여 User에게 맞춤 Video를 제공하는 것이다.</p>
<p>&ensp;Ranking을 수행하는 과정에서 후보 생성 과정보다 더 많은 Feature들을 사용할 수 있는데, 수백만개의 Video를 평가하는 후보 생성 과정과는 다르게 수백개의 Video를 평가하기 때문이다.</p>
<p>&ensp;Ranking 시스템은 Candidate Generation과 비슷한 구조를 사용하며 로지스틱 회귀를 통해 각 Video에 대해 점수를 부여하는 방식으로 작동한다. User에게 Serving 될 때에는 A/B Test를 통해 노출 당 시청 시간을 측정하여 최종 Ranking 결과를 조정한다. 단순히 CTR(Click-Through Rate)을 반영할 경우에는 Clickbait에 영향을 받을 수 있기 때문이다.</p>
<h3 id="41-feature-representation">4.1 Feature Representation</h3>
<p>&ensp;YouTube에서도 categorical, ordinal Feature들을 사용하고 있다. categorical feature의 경우 적게는 2개의 범주, 많게는 수백개의 범주를 가진 feature가 있으며 단일 값을 가지는 경우와 다중 값을 가지는 경우 등이 있다.</p>
<p>&ensp;또한, Item의 속성과 관련이 있는 Feature를 &quot;impression feature&quot;, User, Context와 관련이 있는 Feature를 &quot;query feature&quot;로 구분하며 impression feature는 각각의 item마다 계산되고 query feature는 Ranking이 요청될 때 한번씩 계산되는 특징이 있다.</p>
<p><strong>Feature Engineering</strong>
&ensp;앞서 말한 것처럼 Ranking Model은 수백개의 Feature를 사용하고 categorical feature와 continuous feature를 대략 절반 정도로 나눠서 사용하고 있다.</p>
<p>&ensp;YouTube는 Deep Learning을 사용했지만, YouTube의 row data들은 직접적으로 사용되기 어렵기 때문에 Feature Engineering에 여전히 많은 자원을 투입하고 있다.</p>
<p>&ensp;주요한 과제 중 하나는 User의 Action를 시간적 특성으로 나타내고 이 Action이 Ranking에서 제공하는 Video와 얼마나 연관있는지를 나타내는 것이다.</p>
<p>&ensp;가장 중요한 siganl은 User와 item, 이 item과 유사한 item과의 과거 상호작용이다. 예를 들어, 추천된 Video가 업로드 된 YouTube 채널과 User의 기록을 살펴보는 것이다. 이 User가 이 채널에서 얼마나 많은 영상을 봤는지, 언제 마지막으로 이 주제와 관련된 영상을 봤는지 등을 확인하는 데 이러한 continuous feature는 다른 item을 추천하는 데 있어서 매우 효과적이다. 그리고 어떤 Candidate Generation에서 왔는지, candidate가 생성될 때 score는 얼마인지와 같은 propagate imformation도 중요하다.</p>
<p>&ensp;마지막으로 Video 노출의 빈도를 설명하는 Feature는 추천 시스템에 변화를 주는 데 중요하다. 만약 User가 Video를 추천 받았지만 그것을 시청하지 않는 경우, 그 즉시 Model은 해당 item이 다음 페이지에 노출되도록 만든다.</p>
<p><strong>Embedding Categorical Features</strong>
&ensp;Candidate Generation과 비슷하게 Ranking에서도 Embedding을 사용한다. Vocabulary라고 불리는 unique ID는 고유한 값에 따라 log에 비례하도록 증가하도록 Embedding된다. Cardinality가 매우 높은 Feature의 경우, 클릭 빈도수를 기반으로 상위 N개까지 잘린다. 상위 N개 이외의(Out-of-vocabulary) 값들은 일괄적으로 단순하게 zero-embedding 처리하며 candidate와 마찬가지로 multivalent feature는 average하여 처리한다.</p>
<p><strong>Normalizing Continuous Features</strong>
&ensp;Neural network는 input의 scale과 distribution에 매우 민감하기 때문에 Normalization이 필요함. YouTube는 continuous feature를 0과 1사이의 범위에서 균등하게 분포되도록 설정하였으며 $$\tilde{x}^2$$과 $$\sqrt{\tilde{x}}$$도 함께 input 값으로 사용하여 offline 환경에서 성능을 높일 수 있도록 했다.</p>
<h3 id="42-modeling-expected-watch-time">4.2 Modeling Expected Watch Time</h3>
<p>&ensp;YouTube Ranking model의 목표는 Positive(추천한 Video clicked), Negative(추천한 Video not clicked)를 통해 User의 예상 시청 시간을 예측하는 것이며 이를 위해서 weighted logistic regression을 사용했다.</p>
<p>&ensp;Positive는 Video의 시청 시간에 따라 가중치를 부여 받으며 Negative는 Unit Weight를 받는다. 이러한 방식으로 logistic regression에 의해 학습된 odds는 $$N-k$$에 대한 $$T_i$$의 합계가 된다. 여기서 $$N$$은 학습 데이터의 수, $$k$$는 Positive impression의 수, $$T_i$$는 $$i$$번째 impression의 시청 시간이다. 실제 상황에서 positive impression의 비율은 매우 작기 때문에 odds는 $$E<a href="1+P">T</a>$$에 가까우며 $$P$$는 클릭 확률, $$E[T]$$는 impression의 예상 시청 시간입니다. $$P$$가 작기 때문에 $$E<a href="1+P">T</a>$$의 값은 $$E[T]$$에 가깝다. 최종 예측의 단계에서 활성 함수로 $$e^x$$를 사용하며 변환된 odds는 예상 시청 시간을 근사하여 User가 특정 Video를 얼마나 오래 시청할지 예측하는 데 사용된다.</p>
<h2 id="5-conclusions">5. CONCLUSIONS</h2>
<p>&ensp;Deep Learning을 활용한 CF 모델은 이전에 YouTube에서 사용한 MF 모델 보다 뛰어난 성능을 보여줬다. YouTube는 단순히 User의 최근 검색 기록이나 특정 Video 시청을 학습하기 보다는 보다 일반적인 User Action을 사용하여 과적합 문제를 해결하고 live metric에서 좋은 성능을 보이는 Model을 제시했다.</p>
<p>&ensp;Example Age Feature를 사용하여 편향을 제거하고 모델이 인기 동영상의 시간적 특성을 더 잘 반영할 수 있도록 했다. 이것은 오프라인 테스트에서 정밀도를 향상시키고, A/B 테스트에서 최근 업로드된 동영상의 시청 시간을 크게 늘릴 수 있음을 입증했다.</p>
<p>&ensp;Logistic regression 방식을 통해 Positive와 Negative에 각각 가중치를 부여하였으며, 이것은 직접 CTR을 예측하는 것에 비해 훨씬 더 좋은 성능을 보였다.</p>
<h2 id="review">Review</h2>
<p>&ensp;개인맞춤형 서비스의 중요성이 증대되고 있는 시점에서 추천 알고리즘은 개인화와 떨어질 수 없는 것이다. 특히 추천 알고리즘으로 매우 유명한 기업을 꼽으라면 Netflix와 YouTube인데, 이 논문은 2016년 발표된 것으로 기존의 방식에서 Deep Learning을 도입하여 성능을 향상시킨 사례를 다루고 있다.</p>
<p>&ensp;Deep Learning을 도입했지만 YouTube가 Feature Engineering에도 엄청난 자원을 쏟고 있다는 점을 보며, Feature에 대한 중요성을 깨닫게 되는 계기가 되었다.</p>
<p>&ensp;YouTube의 추천 시스템 논문은 2019년에도 발표된 바가 있다. 다음에는 이 논문을 읽고 2016년의 시스템과 어떤 차이가 있는지를 알아보도록 하자!</p>
<hr>
<p><a href="https://yamalab.tistory.com/124">YouTube Recommendation system 트릴로지 리뷰 - [2]</a>
<a href="https://jonhyuk0922.tistory.com/160">[논문리뷰] Deep Neural Networks for YouTube Recommendations 논문 리뷰</a>
<a href="https://www.blossominkyung.com/recommendersystem/youtube-second">유튜브 추천 시스템-2. DNN for YouTubue</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Programmers] 기사단원의 무기]]></title>
            <link>https://velog.io/@kyeonghun_kim/Programmers-%EA%B8%B0%EC%82%AC%EB%8B%A8%EC%9B%90%EC%9D%98-%EB%AC%B4%EA%B8%B0</link>
            <guid>https://velog.io/@kyeonghun_kim/Programmers-%EA%B8%B0%EC%82%AC%EB%8B%A8%EC%9B%90%EC%9D%98-%EB%AC%B4%EA%B8%B0</guid>
            <pubDate>Tue, 28 May 2024 06:55:15 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h4 id="문제">문제</h4>
<p>숫자나라 기사단의 각 기사에게는 1번부터 number까지 번호가 지정되어 있습니다. 기사들은 무기점에서 무기를 구매하려고 합니다.</p>
</blockquote>
<p>각 기사는 자신의 기사 번호의 약수 개수에 해당하는 공격력을 가진 무기를 구매하려 합니다. 단, 이웃나라와의 협약에 의해 공격력의 제한수치를 정하고, 제한수치보다 큰 공격력을 가진 무기를 구매해야 하는 기사는 협약기관에서 정한 공격력을 가지는 무기를 구매해야 합니다.</p>
<blockquote>
</blockquote>
<p>예를 들어, 15번으로 지정된 기사단원은 15의 약수가 1, 3, 5, 15로 4개 이므로, 공격력이 4인 무기를 구매합니다. 만약, 이웃나라와의 협약으로 정해진 공격력의 제한수치가 3이고 제한수치를 초과한 기사가 사용할 무기의 공격력이 2라면, 15번으로 지정된 기사단원은 무기점에서 공격력이 2인 무기를 구매합니다. 무기를 만들 때, 무기의 공격력 1당 1kg의 철이 필요합니다. 그래서 무기점에서 무기를 모두 만들기 위해 필요한 철의 무게를 미리 계산하려 합니다.</p>
<blockquote>
</blockquote>
<p>기사단원의 수를 나타내는 정수 number와 이웃나라와 협약으로 정해진 공격력의 제한수치를 나타내는 정수 limit와 제한수치를 초과한 기사가 사용할 무기의 공격력을 나타내는 정수 power가 주어졌을 때, 무기점의 주인이 무기를 모두 만들기 위해 필요한 철의 무게를 return 하는 solution 함수를 완성하시오.</p>
<blockquote>
<h4 id="제한사항">제한사항</h4>
</blockquote>
<ul>
<li>1 ≤ number ≤ 100,000</li>
<li>2 ≤ limit ≤ 100</li>
<li>1 ≤ power ≤ limit</li>
</ul>
<h3 id="풀이">풀이</h3>
<ol>
<li>첫번째 풀이 --&gt; 약수의 개수를 구하는 과정에서 시간 초과</li>
</ol>
<pre><code class="language-python">def divisor(n):
    count = 0
    for i in range(1, n + 1)):
        if (n % i == 0):
            count += 1
    return count

def solution(number, limit, power):
    answer = 0
    for i in range(1, number+1):
        if divisor(i) &gt; limit:
            answer += power
        else:
            answer += divisor(i)
    return answer</code></pre>
<ol start="2">
<li>두번째 풀이 --&gt; 약수의 개수를 구하는 과정에서 제곱근을 활용함.<pre><code class="language-python">def divisor(n):
 count = 0
 for i in range(1, int(n ** (1/2) + 1)):
     if (n % i == 0):
         count += 1
         if i &lt; (n // i):
                count += 1
 return count
</code></pre>
</li>
</ol>
<p>def solution(number, limit, power):
    answer = 0
    for i in range(1, number+1):
        if divisor(i) &gt; limit:
            answer += power
        else:
            answer += divisor(i)
    return answer</p>
<pre><code>
3. ChatGPT 풀이
- 시간복잡도 측면에서 ChatGPT가 제시한 코드가 더 빠르고, 특히 number의 숫자가 클 경우 더 빠른 측면이 있음.
```python
def calculate_divisors_upto(n):
    # 0부터 n까지의 모든 수에 대한 약수의 개수를 저장할 리스트
    divisors = [0] * (n + 1)
    for i in range(1, n + 1):

        # i의 배수인 j에 대해 반복
        for j in range(i, n + 1, i): 

            # j는 i로 나누어 떨어지므로 j의 약수 개수에 1을 더함
            divisors[j] += 1 
    return divisors

def solution(number, limit, power):
    answer = 0
    divisors = calculate_divisors_upto(number)
    for i in range(1, number + 1):
        if divisors[i] &gt; limit:
            answer += power
        else:
            answer += divisors[i]
    return answer</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[Programmers] 올바른 괄호]]></title>
            <link>https://velog.io/@kyeonghun_kim/Progrmmers-%EC%98%AC%EB%B0%94%EB%A5%B8-%EA%B4%84%ED%98%B8</link>
            <guid>https://velog.io/@kyeonghun_kim/Progrmmers-%EC%98%AC%EB%B0%94%EB%A5%B8-%EA%B4%84%ED%98%B8</guid>
            <pubDate>Sun, 26 May 2024 08:58:13 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h4 id="문제">문제</h4>
<p>괄호가 바르게 짝지어졌다는 것은 &#39;(&#39; 문자로 열렸으면 반드시 짝지어서 &#39;)&#39; 문자로 닫혀야 한다는 뜻입니다. 예를 들어</p>
</blockquote>
<p>&quot;()()&quot; 또는 &quot;(())()&quot; 는 올바른 괄호입니다.
&quot;)()(&quot; 또는 &quot;(()(&quot; 는 올바르지 않은 괄호입니다.
&#39;(&#39; 또는 &#39;)&#39; 로만 이루어진 문자열 s가 주어졌을 때, 문자열 s가 올바른 괄호이면 true를 return 하고, 올바르지 않은 괄호이면 false를 return 하는 solution 함수를 완성해 주세요.</p>
<blockquote>
</blockquote>
<h4 id="제한-사항">제한 사항</h4>
<ul>
<li>문자열 s의 길이 : 100,000 이하의 자연수</li>
<li>문자열 s는 &#39;(&#39; 또는 &#39;)&#39; 로만 이루어져 있습니다.</li>
</ul>
<h3 id="풀이">풀이</h3>
<ol>
<li><p>첫번째 풀이
while문과 replace를 활용하여 코드를 작성함. --&gt; 정확성 테스트는 통과했으나 시간 복잡도에서 문제가 발생함.</p>
<pre><code class="language-python">def solution(s):
 answer = True
 while True:
     if &quot;()&quot; in s:
         s = s.replace(&quot;()&quot;, &quot;&quot;)
     else:
         break
 if s:
     return False
 else:
     return True</code></pre>
</li>
<li><p>두번째 풀이
Stack을 활용하여 시간 복잡도 문제를 해결함.</p>
<pre><code class="language-python">def solution(s):
 stack = []

 for i in s:
     if stack == []:
         stack.append(i)
     elif stack[-1] + i == &quot;()&quot;:
         stack.pop()
     else:
         stack.append(i)
 if stack:
     return False
 else: return True</code></pre>
</li>
</ol>
<p>&ensp;Code를 실행시킨 결과 정답을 맞출 수 있었으나, 좀 더 개선할 방안을 고민해보았다.</p>
<pre><code class="language-python">def solution(s):
    stack = []


    for c in s: # char의 c로 변경
        if not stack: # if stack == []를 좀 더 Pythonic하게 변경
            stack.append(c)
        elif stack[-1] + s == &quot;()&quot;:
            stack.pop()
        else:
            stack.append()
    return not stack # 마지막 return 조건을 간단하게 변경</code></pre>
<ol start="3">
<li>ChatGPT가 작성한 결과<pre><code class="language-python">def solution(s):
 stack = []
 for char in s:
     if char == &#39;(&#39;:
         stack.append(char)
     elif char == &#39;)&#39;:
         if stack:
             stack.pop()
         else:
             return False
 return not stack</code></pre>
</li>
</ol>
<p>&ensp;시간 복잡도 측면에서 약 절반 가량의 시간을 절약하고 있으며, 좀 더 간결하고 명확한 코드를 작성하고 있음.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[읽을 거리]논문을 읽는 법]]></title>
            <link>https://velog.io/@kyeonghun_kim/%EC%9D%BD%EC%9D%84-%EA%B1%B0%EB%A6%AC%EB%85%BC%EB%AC%B8%EC%9D%84-%EC%9D%BD%EB%8A%94-%EB%B2%95</link>
            <guid>https://velog.io/@kyeonghun_kim/%EC%9D%BD%EC%9D%84-%EA%B1%B0%EB%A6%AC%EB%85%BC%EB%AC%B8%EC%9D%84-%EC%9D%BD%EB%8A%94-%EB%B2%95</guid>
            <pubDate>Sat, 27 Apr 2024 08:43:37 GMT</pubDate>
            <description><![CDATA[<h1 id="🤷🏻-논문을-읽는-법">🤷🏻 논문을 읽는 법</h1>
<p>&ensp;논문과는 거리가 멀다고 생각했지만 공부를 계속하며 논문을 읽어야할 일이 많이 생겼다. 영어와 어려운 용어 등 논문을 힘겹게 읽다가 다른 사람들은 어떻게 논문 리뷰를 하는지 검색했다. 수많은 꿀팁과 논문을 읽는 방법을 소개한 논문을 발견, 정리를 해보자!</p>
<h2 id="✏️-논문을-읽는-span-stylecolorf2b13e이유는-무엇span인가">✏️ 논문을 읽는 <span style="color:#F2B13E">이유는 무엇</span>인가?</h2>
<p>&ensp;논문을 읽는 이유는 보통 <span style="color:#F2B13E"><strong>업계의 동향</strong></span>을 파악하거나, 프로젝트, 업무를 수행하는 데 필요한 <span style="color:#F2B13E"><strong>기술을 적용</strong></span>시키기 위해서라고 생각한다. </p>
<p>&ensp;</p>
<h2 id="✏️-span-stylecolorf2b13e어떤-것span을-읽을-것인가">✏️ <span style="color:#F2B13E">어떤 것</span>을 읽을 것인가?</h2>
<ol>
<li><strong>리뷰 논문</strong>
리뷰를 하기 위해서는 어떤 것을 리뷰할 것인지 먼저 정해야한다. 특정 주제(예를 들면, 추천시스템)를 정했다면 그것에 대한 리뷰 논문을 먼저 찾아 보는 것도 좋다!</li>
</ol>
<blockquote>
<h4 id="❓-리뷰-논문이-뭔가요">❓ 리뷰 논문이 뭔가요?</h4>
<p>&ensp;리뷰 논문이란 특정 주제에 대한 논문을 비평하고 분석하는 글, 주제에 대한 다양한 연구들 간의 공통점, 상반되거나 다른 결과의 이유 등을 설명한다. 이것을 통해 해당 분야의 배경지식, 동향 등을 파악할 수 있다.</p>
</blockquote>
<ol start="2">
<li><strong>학술 정보 사이트</strong>, <strong>주요 학회지</strong> 등을 찾아보자!
<a href="https://scholar.google.co.kr/schhp?hl=en">Google Scholar</a>, <a href="https://citeseerx.ist.psu.edu/">cite ceer</a> 등 학술 정보 사이트에서 검색을 하거나, 주요 학회지 등에서 특정 주제에 대해 검색을 해본다.</li>
</ol>
<blockquote>
<p><strong>ML/DL 분야 주요 학회지 및 컨퍼런스</strong></p>
</blockquote>
<ul>
<li>NIPS, ICML, ICLR(3대 학회지)</li>
<li>ACL, EMNLP(NLP 분야 컨퍼런스)</li>
</ul>
<p>&ensp;</p>
<h2 id="✏️-span-stylecolorf2b13e어떻게span-읽어야-하는가">✏️ <span style="color:#F2B13E">어떻게</span> 읽어야 하는가?</h2>
<h3 id="논문을-읽는-span-stylecolorf2b13e마음가짐span">논문을 읽는 <span style="color:#F2B13E">마음가짐</span></h3>
<p><strong>한 번에 이해하려고 하지 말 것!</strong></p>
<p>처음 읽어보는 주제, 공부를 시작한 분야라면 당연히 논문을 한 번에 이해하기 어려울 것이다. 조급함을 버리고 편안한 마음으로 읽어보자.</p>
<p><strong>전문적인 용어나 개념은 검색해볼 것!</strong>
전문 용어가 이해되지 않아 문장이 잘 읽히지 않을 때가 있다. 그럴 때에는 되도록 구글에 검색하여 영문으로 된 자료를 읽어보자.</p>
<p>구글에 검색했을 때 해당 용어의 배경지식과 관련 연구 자료들이 검색되어 논문을 공부할 때 더 많은 도움이 된다.</p>
<p><strong>디테일에 너무 집착하지말 것!</strong>
한 문단의 논리가 이해되지 않았을 때 과감히 다음 문단으로 넘어가보자. 관련된 논문을 많이 읽어보고 이해도가 쌓이고 나면 저절로 해결되는 경우가 많기 때문이다.</p>
<p>물론, 경험이 쌓였을 때는 디테일까지 챙겨서 충분한 시간을 들여 파고 드는 것도 좋다. 그러나 논문을 처음 읽는다면 그 논문의 요점과 큰 그림만 파악해도 성공적이다.</p>
<p><strong>좋은 논문은 시간 차이를 두고 여러번 읽어볼 것!</strong>
어떤 분야에서 유명한 연구 그룹에서 출간한 논문 또는 선배들이 추천하는 내용이 좋은 논문은 시간 차이를 두고 여러 번 읽어보자. 처음 읽었을 때는 큰 그림을 위주로 봤다면, 두 세 번째 읽을 때는 하나의 디테일에 신경써서 읽어보는 것도 좋다.</p>
<p>단지 시간 차이를 두었을 뿐인데 예전에는 이해되지 않았던 논리가 다시 읽었을 때는 쉽게 이해될 수도 있고 내용이 우수한 논문은 여러 번 읽을수록 논문에 대한 이해도가 높아져 새로운 인사이트를 얻을 수 있다.</p>
<h3 id="논문을-읽는-span-stylecolorf2b13e순서span">논문을 읽는 <span style="color:#F2B13E">순서</span></h3>
<p>논문을 <strong>쓰는 순서</strong>와 <strong>읽는 순서</strong>는 <span style="color:#007C7A"><strong>다르다!</strong></span></p>
<blockquote>
</blockquote>
<p><span style="color:#007C7A"><strong>논문이 쓰여 있는 순서</strong></span>
title ➡️ abstract ➡️ introduction ➡️ experimental
➡️ results &amp; discussion ➡️ conclusion ➡️ references</p>
<blockquote>
</blockquote>
<p><span style="color:#F2B13E"><strong>논문을 읽는 순서</strong></span>
title ➡️ abstract ➡️ introduction의 마지막 ➡️ conclusion ➡️ Results &amp; discussion (data) ➡️ Introduction 전체 ➡️ Results &amp; discussion (detail)</p>
<blockquote>
</blockquote>
<ol>
<li><p><span style="color:#F2B13E"><strong>title</strong></span></p>
<ul>
<li><span style="color:#007C7A"><strong>제목</strong></span>, <span style="color:#007C7A"><strong>저널명</strong></span>, <span style="color:#007C7A"><strong>출간년도</strong></span>, <span style="color:#007C7A"><strong>어떤 그룹에서 낸 것</strong></span>인지 체크</li>
</ul>
</li>
<li><p><span style="color:#F2B13E"><strong>abstract</strong></span></p>
<ul>
<li>논문의 <span style="color:#007C7A"><strong>주요 내용</strong></span>을 한 문단 내지 두 문단으로 정리를 해 놓은 파트</li>
<li>해당 연구가 왜 진행되었는가?, 목표는 무엇인가?, 결론은 무엇인가? 등을 파악할 수 있음.</li>
</ul>
</li>
<li><p><span style="color:#F2B13E"><strong>conclusion</strong></span></p>
<ul>
<li>연구의 <span style="color:#007C7A"><strong>주요 성과</strong></span>들에 대해 약간 더 자세하게 알아볼 수 있음.</li>
<li>초록을 읽으면서 이해가 되지 않았던 부분을 결론 파트에서 보강할 수 있음.</li>
<li>해당 논문의 <span style="color:#007C7A"><strong>큰 그림</strong></span>을 그려볼 수 있음. 이 연구가 왜 진행되었으며 어떤 의미가 있는지 정도는 말할 수 있는 상태.</li>
</ul>
</li>
<li><p><span style="color:#F2B13E"><strong>results &amp; discussion</strong></span></p>
<ul>
<li>각종 <span style="color:#007C7A"><strong>그래프와 표</strong></span>를 훑어보기.</li>
<li>자세하게 데이터를 해석할 필요는 없고, 논문이 어떤 종류의 데이터를 담고 있으며, 어떤 분석 방법을 사용하여 논리를 뒷받침하고 있는지 <span style="color:#007C7A"><strong>대강 파악</strong></span>하는 것이 목적</li>
</ul>
</li>
<li><p><span style="color:#F2B13E"><strong>introduction</strong></span></p>
<ul>
<li>해당 분야에 대한 전반적인 <span style="color:#007C7A"><strong>배경지식</strong></span>이 소개되어 있음.</li>
<li>저자가 참고한 선행 연구들에 대한 분석과 함께 <span style="color:#007C7A"><strong>연구 동기</strong></span>와 <span style="color:#007C7A"><strong>방향성</strong></span>에 대해 알아볼 수 있음.</li>
<li>해당 분야에서 사실이라고 받아들여지고 있는 여러 이론들과 연구 결과들을 공부할 수 있는 파트.</li>
</ul>
</li>
<li><p><span style="color:#F2B13E"><strong>results &amp; discussion</strong></span></p>
<ul>
<li>다시 읽는 만큼 데이터를 해석하는 저자의 논리의 흐름을 <span style="color:#007C7A"><strong>디테일하게 파악</strong></span>.</li>
<li>이해하기 어려운 부분이 있다면 저자가 표시해 놓은 관련된 문헌 번호를 reference 파트에서 찾아 따로 표시를 해놓고 다음에 공부할 논문으로 준비해놓기</li>
</ul>
</li>
</ol>
<h2 id="마치며">마치며...</h2>
<p>&ensp;구글링을 하며 찾은 여러 사람들의 논문 리뷰 방법을 정리 했다. 방법을 아는 것보다 더 중요한 것은 이것을 <span style="color:red"><strong>꾸준히 실천</strong></span>하는 것에 있다고 생각한다. 효율적으로 공부하는 방법이 생긴만큼 <span style="color:red"><strong>성실하자!</strong></span></p>
<hr>
<p>©️</p>
<ul>
<li><a href="https://velog.io/@kay0710/ftlgio3z">논문 리뷰는 어떻게 할까?</a></li>
<li><a href="https://woongheelee.com/entry/%EB%85%BC%EB%AC%B8%EC%9D%84-%ED%9A%A8%EC%9C%A8%EC%A0%81%EC%9C%BC%EB%A1%9C-%EC%9D%BD%EB%8A%94-%EB%B0%A9%EB%B2%95">논문을 효율적으로 읽는 방법</a></li>
<li><a href="https://journey-for-phd.tistory.com/6">[대학원 꿀팁] 논문 읽는 법(feat. 논문 리뷰 방법)</a></li>
<li><a href="http://svr-sk818-web.cl.cam.ac.uk/keshav/papers/07/paper-reading.pdf">Keshav, S. (2007). How to read a paper. ACM SIGCOMM Computer Communication Review, 37(3), 83-84.</a></li>
<li><a href="https://velog.io/@jeewoo1025/MLDL-%EC%A3%BC%EC%9A%94-%ED%95%99%ED%9A%8C%EC%A7%80">MLDL-주요-학회지</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ML/DL]추천시스템 알아보기 -1]]></title>
            <link>https://velog.io/@kyeonghun_kim/MLDL%EC%B6%94%EC%B2%9C%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0-1</link>
            <guid>https://velog.io/@kyeonghun_kim/MLDL%EC%B6%94%EC%B2%9C%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0-1</guid>
            <pubDate>Sat, 20 Apr 2024 12:36:24 GMT</pubDate>
            <description><![CDATA[<h2 id="✍🏻-추천시스템이란">✍🏻 추천시스템이란?</h2>
<p>&ensp;추천시스템이란 정보 필터링 기술의 일종으로, 특정 사용자가 관심을 가질만한 정보 (영화, 음악, 책, 뉴스, 이미지, 웹 페이지 등)를 추천하는 것을 말합니다.</p>
<p>&ensp;사용자의 선호도 및 과거 행동을 토대로 사용자에 적합한 콘텐츠를 제공하는 것이 주된 목표이며 대표적인 예시로 유튜브의 동영상 추천, 넷플릭스의 컨텐츠 추천, X(트위터)의 트윗 추천 등이 있습니다.</p>
<h4 id="즉-추천시스템이란-사용자user에-맞는-상품item을-추천해주는-것이다">즉, 추천시스템이란 사용자(User)에 맞는 상품(Item)을 추천해주는 것이다!</h4>
<blockquote>
</blockquote>
<h4 id="💡-추천시스템의-필요성">💡 추천시스템의 필요성</h4>
<p>&ensp;하루에도 수많은 플랫폼에서 엄청난 수의 콘텐츠가 생산되고 있으며, 이러한 상황에서 본인이 정확하게 원하는 콘텐츠가 있지 않은 이상 콘텐츠를 고르기까지 많은 시간이 걸립니다. 이러한 소비자들의 불편한 부분을 해결하고자 인공지능 기반의 추천 시스템을 구현하여 소비자들에게 개인화된 콘텐츠를 제공하고 있습니다.</p>
<blockquote>
</blockquote>
<p>&ensp;넷플릭스 같은 경우 구독 회원의 약 70~80%가 추천 시스템 기반으로 추천된 콘텐츠를 시청하며, 아마존 구매 중 35%는 아마존이 추천한 제품을 구매하고 있다고 알려져 있습니다.</p>
<blockquote>
</blockquote>
<h2 id="✍🏻-추천시스템의-종류는-무엇이-있나">✍🏻 추천시스템의 종류는 무엇이 있나?</h2>
<p>&ensp; 대표적인 추천시스템 알고리즘은 다음과 같습니다.</p>
<blockquote>
</blockquote>
<ul>
<li>Content - based Filtering (콘텐츠 기반 추천 시스템)</li>
<li>Collaborative Filtering (협업 필터링)<ul>
<li>Memory-Based Algorithm(사용자 기반, 아이템 기반)</li>
<li>Model-Based Algorithm(행렬 분해)</li>
</ul>
</li>
</ul>
<h3 id="✏️-content---based-filtering">✏️ Content - based Filtering</h3>
<h4 id="개요">개요</h4>
<p>&ensp;콘텐츠 기반 필터링은 아이템 콘텐츠에 대한 정보를 바탕으로 추천을 만들어내는 방식입니다. Content란 Text, Audio, image 등과 같은 아이템의 성질, Feature입니다.</p>
<p>&ensp;컨텐츠 기반 필터링은 TF-IDF, Word2Vec(NLP에서만 사용 되는 방법)와 같은 Feature Extraction, 머실러닝 알고리즘 등이 사용됩니다. 아이템 간의 유사도는 코사인 유사도, 유클리어드 유사도, 자카드 유사도, 피어슨 상관관계를 통해 측정되고 유사도 함수들을 통해 각각 순위를 매긴 후 정렬을 하여 사용자에게 추천합니다.</p>
<h4 id="즉-사용자가-과거에-선호했던-아이템의-콘텐츠를-분석하여-이것과-비슷한-아이템을-추천하는-것입니다">&ensp;즉, 사용자가 과거에 선호했던 아이템의 콘텐츠를 분석하여 이것과 비슷한 아이템을 추천하는 것입니다.</h4>
<h4 id="장점과-단점">장점과 단점</h4>
<p>&ensp;콘텐츠 기반 필터링의 장점은 세 가지 정도가 있습니다. 첫째, 다른 사용자의 데이터가 존재하지 않더라도 신규 사용자에게 콘텐츠를 추천할 수 있습니다. 그래서 Cold Start 문제나 Sparsity 문제에서 자유롭습니다. 둘째, 추천하는 콘텐츠에 대한 근거를 제시할 수 있습니다. 콘텐츠 간의 유사성을 계산하여 정렬하는 방식을 취하기 때문입니다. 세번째, 새로 추가된 콘텐츠나 유명하지 않은 콘텐츠도 추천이 가능합니다.</p>
<p>&ensp;단점으로는 사용자의 과거 행동 데이터가 없는 경우 콘텐츠를 추천하는 데 어려움이 따릅니다. 또한 콘텐츠의 유사성을 계산하여 추천하기 때문에 사용자가 이미 알고 있는 유사한 콘텐츠만을 추천하는 문제가 발생할 수 있습니다.</p>
<h3 id="✏️-collaborative-filtering">✏️ Collaborative Filtering</h3>
<h4 id="개요-1">개요</h4>
<p>&ensp;협업 필터링(Collaborative Filtering)은 많은 사용자로부터 수집한 행동데이터(구매패턴, 평점 등)을 기반으로 하여 다른 사용자에게 콘텐츠를 추천해 주는 방법입니다. 협업 필터링 기술의 중요한 가정은 &quot;많은 사용자로부터 얻은 정보를 토대로 나와 비슷한 취향을 가진 사람들이 선호하는 콘텐츠를 나도 좋아할 가능성이 크다.&quot;입니다. 다시 말해, 집단 지성의 개념과 매우 유사하다고 볼 수 있습니다. 협업 필터링 방법에는 메모리 기반 알고리즘(Memory Based Algorithm)과 모델 기반 알고리즘(Model Based Algorithm)이 있습니다.</p>
<h4 id="memory-based-algorithm">Memory Based Algorithm</h4>
<p>&ensp;가장 전통적인 접근 방식으로 사용자 기반 필터링(User Based Collaborative Filtering), 아이템 기반 필터링(Item Based Collaborative Filtering)이 있습니다. 사용자 기반 필터링은 사용자 간의 유사도를 기준으로 추천해 주는 방법으로 각 사용자들의 평가 이력, 선호도 등을 사용하는 방법입니다. 아이템 기반 필터링은 아이템 간의 유사도를 기준으로 추천해 주는 방법으로 특정 아이템을 좋아했던 내역을 보고 이를 비슷하게 평가한 유저에게 추천을 하는 방식입니다.</p>
<h4 id="model-based-algorithm">Model Based Algorithm</h4>
<p>&ensp;잠재 요인(Latent Factor) 협업 필터링이라고도 불리며 현재에도 자주 쓰이는 방법입니다. 이것은 사용자와 아이템 간의 평점 행렬 속에 숨어 있는 잠재 요인 행렬을 추출하여 내적 곱을 통해 사용자가 평가하지 않은 항목들에 대한 평점까지 예측하여 추천하는 방법입니다. 행렬 분해(Matrix Factorization)라는 방법을 통해 큰 다차원 행렬을 차원 감소시키는 과정에서 행렬에 포함되어 있는 잠재 요인을 추출할 수 있습니다.</p>
<p>&ensp;행렬 분해의 기본적 아이디어는 &quot;사용자의 선호도가 몇개의 Hidden Factors&quot;로 결정 될 수 있다는 것입니다. 이러한 Factors를 Embeddings라고 부릅니다.</p>
<h4 id="장점과-단점-1">장점과 단점</h4>
<p>&ensp;협업 필터링의 장점으로는 아이템의 상세 정보 없이 추천이 가능하고 사용자에게 놀라움을 줄 수 있다는 장점이 있습니다.</p>
<p>&ensp;단점으로는 사용자 행동 데이터가 부족한 시기인 초기 단계에서는 추천이 어렵다는 점(Cold Start)과 사용자와 아이템의 수가 많아질수록 연산해야할 행렬이 기하급수적으로 늘어나기 때문에 효율성이 떨어진다는 점이 있습니다. 또한, 사용자들이 소수의 인기 있는 항목에만 관심을 보여서 관심이 저조한 항목은 추천되지 못하는 문제, 소수의 인기 콘텐츠가 전체 콘텐츠 비율을 차지하는 현상이 나타날 수 있습니다.</p>
<hr>
<p>©️ 참고자료
<a href="https://msmskim.tistory.com/25">https://msmskim.tistory.com/25</a>
<a href="https://blog.insilicogen.com/61">https://blog.insilicogen.com/61</a>
<a href="https://calmmimiforest.tistory.com/100">https://calmmimiforest.tistory.com/100</a>
<a href="https://velog.io/@mineru/%EC%B6%94%EC%B2%9C-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%B6%94%EC%B2%9C-%EC%8B%9C%EC%8A%A4%ED%85%9C%EC%9D%B4%EB%9E%80#%EA%B7%B8%EB%9F%BC-%EC%B6%94%EC%B2%9C-%EC%8B%9C%EC%8A%A4%ED%85%9C%EC%9D%80-%EB%AD%90%EB%8B%88">https://velog.io/@mineru/추천-시스템-추천-시스템이란#그럼-추천-시스템은-뭐니</a>
<a href="https://velog.io/@jochedda/%EC%B6%94%EC%B2%9C-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%B6%94%EC%B2%9C-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%A2%85%EB%A5%98">https://velog.io/@jochedda/추천-시스템-추천-알고리즘-종류</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Algorithm]슬라이딩 윈도우(Sliding Window)]]></title>
            <link>https://velog.io/@kyeonghun_kim/Algorithm%EC%8A%AC%EB%9D%BC%EC%9D%B4%EB%94%A9-%EC%9C%88%EB%8F%84%EC%9A%B0Sliding-Window</link>
            <guid>https://velog.io/@kyeonghun_kim/Algorithm%EC%8A%AC%EB%9D%BC%EC%9D%B4%EB%94%A9-%EC%9C%88%EB%8F%84%EC%9A%B0Sliding-Window</guid>
            <pubDate>Thu, 07 Mar 2024 09:59:59 GMT</pubDate>
            <description><![CDATA[<h3 id="✏️-슬라이딩-윈도우란">✏️ 슬라이딩 윈도우란?</h3>
<p>&ensp; 데이터 구조나 배열을 순차적으로 탐색하는 알고리즘으로 <strong>고정 크기</strong>의 윈도우를 유지하면서 필요한 작업을 수행하는 기법이다. 이 알고리즘은 배열이나 리스트 등의 순차적 데이터 구조를 다루는 경우에 유용하게 활용된다.</p>
<h4 id="슬라이딩-윈도우의-예시">슬라이딩 윈도우의 예시</h4>
<p>&ensp; [3, -2, -4, -9, 0, 3, 7, 13, 8, -3]라는 배열이 있다고 했을 때, &#39;4&#39;라는 고정된 길이의 부분합을 구한다고 가정한다. 이때 부분의 합들은 (3, -2, -4, -9), (-2, -4, -9, 0) ... 등으로 구할 수 있다.</p>
<p>&ensp; 여기서 식을 자세히 살펴보면 중복된 부분이 있는데 계산된 합에서 첫번째 인덱스를 빼고, 다음 인덱스를 더하는 식으로 중복되고 있다.</p>
<h4 id="python-구현">Python 구현</h4>
<pre><code class="language-python"># window 중 가장 큰 합을 구하는 과정

arr = [3, -2, -4, -9, 0, 3, 7, 13, 8, -3] # 예시 array
size = 2 # 고정된 크기
n = len(arr)

window = sum(arr[:size])
answer = window

for i in range(size, n):
    window += arr[i] - arr[i - size]
    answer = max(answer, window)
print(answer)

&gt;&gt;&gt; 21</code></pre>
<h3 id="슬라이딩-윈도우의-장점">슬라이딩 윈도우의 장점</h3>
<ol>
<li>이 방법은 고정된 크기의 배열을 다룰 때 유용하게 사용할 수 있다.</li>
<li>일반적으로 $$O(n)$$의 시간복잡도를 가지며 대부분의 경우에서 효율적이다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Algorithm]이진 탐색(Binary Search)]]></title>
            <link>https://velog.io/@kyeonghun_kim/Algorithm%EC%9D%B4%EC%A7%84-%ED%83%90%EC%83%89Binary-Search</link>
            <guid>https://velog.io/@kyeonghun_kim/Algorithm%EC%9D%B4%EC%A7%84-%ED%83%90%EC%83%89Binary-Search</guid>
            <pubDate>Tue, 27 Feb 2024 05:48:14 GMT</pubDate>
            <description><![CDATA[<h3 id="✏️-이진-탐색이란">✏️ 이진 탐색이란?</h3>
<p>&ensp; 이진 탐색(binary search)란 <strong>정렬된</strong> 데이터에서 특정한 값을 찾아내는 방법이다. 전체를 절반으로 구분해서 현재 자기 위치가 찾는 위치보다 값이 큰지 작은지에 따라 탐색 방향을 결정하는데, 핵심은 필요 없는 부분을 탐색하지 않는 것이다.</p>
<p>&ensp; 정렬된 데이터는 일정한 순서로 나열되어 있기 때문에 찾는 위치가 지금 위치보다 뒤에 있다면 그보다 앞의 데이터는 찾아보지 않아도 상관이 없다. 따라서 중앙에서부터 값을 비교하기 시작해서 매 탐색마다 탐색 범위가 절반씩 줄어든다.</p>
<p>&ensp; 이진 탐색의 가장 큰 장점은 데이터가 정렬되어 있다면 시간복잡도가 $O(logn)$로 줄어든다는 점이다. 따라서 문제에서 주어지는 입력의 크기가 크다면 이진 탐색을 사용하는 것이 좋다.</p>
<h3 id="✍🏻-구현-방법">✍🏻 구현 방법</h3>
<pre><code class="language-python">def binarySearch(data, target):
    start = 0
    end = len(data) - 1

    while start &lt;= end:
        mid = (start + end) // 2

        if data[mid] == target:
            return mid
        elif data[mid] &lt; target:
            start = mid + 1
        else:
            end = mid - 1
    return -1 # 탐색에 실패하면 -1 반환

my_list = [1, 2, 3, 7, 9, 11, 33]
print(binarySearch(my_list, 3))
&gt;&gt;&gt; 2</code></pre>
<h3 id="✍🏻-파라메트릭-서치parametric-search">✍🏻 파라메트릭 서치(parametric search)</h3>
<p>&ensp; 파라메트릭 서치란 정렬 없이 이진 탐색 개념만 사용해 문제를 푸는 것이다. 현재의 상황에서 어떻게 나아갈 것인지를 판단하는 방식으로 이진 탐색을 응용하는 방법이다.</p>
<p>&ensp; 파라메트릭 서치는 매개변수를 사용하여 예/아니오 방식처럼 두 갈래로 나눠 탐색하는 방법을 의미한다. 선택지가 결정되었을 때 실행할 기능을 함수로 만든 뒤, 선택지를 함수의 매개변수로 제공하여 그 값에 따라 탐색 방향을 결정하는 것이다. 다만, <strong>주어진 기준에서 최댓값/최솟값을 구할 수 있어</strong>야 하고 <strong>정렬 형태</strong>(1번 조건을 만족하면 그보다 작은 값, 큰 값도 1번 조건을 만족해야 함)로 구성되어야 한다.</p>
<hr>
<p>© 참고
<a href="https://cjh5414.github.io/binary-search/">https://cjh5414.github.io/binary-search/</a>
<a href="https://code-angie.tistory.com/3">https://code-angie.tistory.com/3</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[BackJoon] 10816번 숫자 카드 2]]></title>
            <link>https://velog.io/@kyeonghun_kim/BackJoon-%EC%88%AB%EC%9E%90-%EC%B9%B4%EB%93%9C-2</link>
            <guid>https://velog.io/@kyeonghun_kim/BackJoon-%EC%88%AB%EC%9E%90-%EC%B9%B4%EB%93%9C-2</guid>
            <pubDate>Tue, 20 Feb 2024 08:21:37 GMT</pubDate>
            <description><![CDATA[<h3 id="전체-문제-보기"><a href="https://www.acmicpc.net/problem/10816">전체 문제 보기</a></h3>
<blockquote>
</blockquote>
<h4 id="문제">문제</h4>
<p>숫자 카드는 정수 하나가 적혀져 있는 카드이다. 상근이는 숫자 카드 N개를 가지고 있다. 정수 M개가 주어졌을 때, 이 수가 적혀있는 숫자 카드를 상근이가 몇 개 가지고 있는지 구하는 프로그램을 작성하시오.</p>
<blockquote>
</blockquote>
<h4 id="입력">입력</h4>
<p>첫째 줄에 상근이가 가지고 있는 숫자 카드의 개수 N(1 ≤ N ≤ 500,000)이 주어진다. 둘째 줄에는 숫자 카드에 적혀있는 정수가 주어진다. 숫자 카드에 적혀있는 수는 -10,000,000보다 크거나 같고, 10,000,000보다 작거나 같다.</p>
<blockquote>
</blockquote>
<p>셋째 줄에는 M(1 ≤ M ≤ 500,000)이 주어진다. 넷째 줄에는 상근이가 몇 개 가지고 있는 숫자 카드인지 구해야 할 M개의 정수가 주어지며, 이 수는 공백으로 구분되어져 있다. 이 수도 -10,000,000보다 크거나 같고, 10,000,000보다 작거나 같다.</p>
<blockquote>
</blockquote>
<h4 id="출력">출력</h4>
<p>첫째 줄에 입력으로 주어진 M개의 수에 대해서, 각 수가 적힌 숫자 카드를 상근이가 몇 개 가지고 있는지를 공백으로 구분해 출력한다.</p>
<h3 id="풀이">풀이</h3>
<ol>
<li>numbers에 등장한 숫자를 딕셔너리 형태로 저장한다. 저장할 때 나온 횟수만큼 1을 증가시킨다.</li>
<li>check를 반복하면서 그 숫자가 있으면 횟수를 출력하고 없으면 0을 출력한다.</li>
</ol>
<pre><code class="language-python"># Bakcjoon 10816번 숫자 카드2

N = int(input())
numbers = list(map(int, input().split()))
M = int(input())
checks = list(map(int, input().split()))

check_dict = {}
for num in numbers:
    if num in check_dict:
        check_dict[num] += 1
    else:
        check_dict[num] = 1

for i in range(M):
    if checks[i] in check_dict.keys():
        print(check_dict[checks[i]], end = &#39; &#39;)
    else:
        print(0, end = &#39; &#39;)</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[BackJoon] 10815번 숫자 카드]]></title>
            <link>https://velog.io/@kyeonghun_kim/BakcJoon%EC%88%AB%EC%9E%90%EC%B9%B4%EB%93%9C</link>
            <guid>https://velog.io/@kyeonghun_kim/BakcJoon%EC%88%AB%EC%9E%90%EC%B9%B4%EB%93%9C</guid>
            <pubDate>Tue, 20 Feb 2024 08:16:43 GMT</pubDate>
            <description><![CDATA[<h3 id="전체-문제-보기"><a href="https://www.acmicpc.net/problem/10815">전체 문제 보기</a></h3>
<blockquote>
</blockquote>
<h4 id="문제">문제</h4>
<p>숫자 카드는 정수 하나가 적혀져 있는 카드이다. 상근이는 숫자 카드 N개를 가지고 있다. 정수 M개가 주어졌을 때, 이 수가 적혀있는 숫자 카드를 상근이가 가지고 있는지 아닌지를 구하는 프로그램을 작성하시오.</p>
<blockquote>
</blockquote>
<h4 id="입력">입력</h4>
<p>첫째 줄에 상근이가 가지고 있는 숫자 카드의 개수 N(1 ≤ N ≤ 500,000)이 주어진다. 둘째 줄에는 숫자 카드에 적혀있는 정수가 주어진다. 숫자 카드에 적혀있는 수는 -10,000,000보다 크거나 같고, 10,000,000보다 작거나 같다. 두 숫자 카드에 같은 수가 적혀있는 경우는 없다.</p>
<blockquote>
</blockquote>
<p>셋째 줄에는 M(1 ≤ M ≤ 500,000)이 주어진다. 넷째 줄에는 상근이가 가지고 있는 숫자 카드인지 아닌지를 구해야 할 M개의 정수가 주어지며, 이 수는 공백으로 구분되어져 있다. 이 수도 -10,000,000보다 크거나 같고, 10,000,000보다 작거나 같다</p>
<blockquote>
</blockquote>
<h4 id="출력">출력</h4>
<p>첫째 줄에 입력으로 주어진 M개의 수에 대해서, 각 수가 적힌 숫자 카드를 상근이가 가지고 있으면 1을, 아니면 0을 공백으로 구분해 출력한다.</p>
<h3 id="풀이">풀이</h3>
<ol>
<li>숫자가 존재하는지 여부만을 판단하기 때문에 중복된 숫자는 set()함수를 이용하여 제거한다.</li>
</ol>
<pre><code class="language-python"># 백준 숫자카드
import sys
N = int(input())
numbers = set(map(int, sys.stdin.readline().split()))
M = int(input())
checks = list(map(int, sys.stdin.readline().split()))

for num in checks:
    if num in numbers:
        print(&quot;1&quot;, end = &quot; &quot;)
    else:
        print(&quot;0&quot;, end = &quot; &quot;)</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[BackJoon] 1764번 듣보잡]]></title>
            <link>https://velog.io/@kyeonghun_kim/BackJoon%EB%93%A3%EB%B3%B4%EC%9E%A1</link>
            <guid>https://velog.io/@kyeonghun_kim/BackJoon%EB%93%A3%EB%B3%B4%EC%9E%A1</guid>
            <pubDate>Tue, 20 Feb 2024 08:00:06 GMT</pubDate>
            <description><![CDATA[<h3 id="전체-문제-보기"><a href="https://www.acmicpc.net/problem/1764">전체 문제 보기</a></h3>
<blockquote>
</blockquote>
<h4 id="문제">문제</h4>
<p>김진영이 듣도 못한 사람의 명단과, 보도 못한 사람의 명단이 주어질 때, 듣도 보도 못한 사람의 명단을 구하는 프로그램을 작성하시오.</p>
<blockquote>
</blockquote>
<h4 id="입력">입력</h4>
<p>첫째 줄에 듣도 못한 사람의 수 N, 보도 못한 사람의 수 M이 주어진다. 이어서 둘째 줄부터 N개의 줄에 걸쳐 듣도 못한 사람의 이름과, N+2째 줄부터 보도 못한 사람의 이름이 순서대로 주어진다. 이름은 띄어쓰기 없이 알파벳 소문자로만 이루어지며, 그 길이는 20 이하이다. N, M은 500,000 이하의 자연수이다.</p>
<blockquote>
</blockquote>
<p>듣도 못한 사람의 명단에는 중복되는 이름이 없으며, 보도 못한 사람의 명단도 마찬가지이다.</p>
<blockquote>
</blockquote>
<h4 id="출력">출력</h4>
<p>듣보잡의 수와 그 명단을 사전순으로 출력한다.</p>
<h3 id="풀이">풀이</h3>
<ol>
<li>입력의 크기가 50만개이므로 이중반복문으로 사용할 때 시간초과가 발생한다.</li>
<li>딕셔너리를 사용해 시간복잡도를 줄여서 탐색을 한다.</li>
</ol>
<h4 id="첫번째-제출">첫번째 제출</h4>
<pre><code class="language-python"># Backjoon 듣보잡

N, M = map(int, input().split())
name_list = []
for _ in range(N+M):
    name = input()
    name_list.append(name)
listen_dict = {}
see_dict = {}
for idx, name in enumerate(name_list):
    if idx &lt; N:
        listen_dict[name] = 1
    else:
        see_dict[name] = 1

check_list = []
for check in listen_dict:
    if check in see_dict:
        check_list.append(check)

print(len(check_list))
for name in sorted(check_list):
    print(name)</code></pre>
<h4 id="set을-사용한-방법">set을 사용한 방법</h4>
<pre><code class="language-python">N, M = map(int, input().split())
name_list = []
for _ in range(N+M):
    name = input()
    name_list.append(name)

# set()을 사용하여 집합으로 변경
listen = set(name_list[:N])
see = set(name_list[N:])
result = sorted(set(listen) &amp; set(see)) # 집합의 연산으로 교집합 구하기

print(len(result),&#39;\n&#39;.join(result),sep=&#39;\n&#39;)</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Programmers] 오픈채팅방 Lv2]]></title>
            <link>https://velog.io/@kyeonghun_kim/Programmers%EC%98%A4%ED%94%88%EC%B1%84%ED%8C%85%EB%B0%A9-Lv2</link>
            <guid>https://velog.io/@kyeonghun_kim/Programmers%EC%98%A4%ED%94%88%EC%B1%84%ED%8C%85%EB%B0%A9-Lv2</guid>
            <pubDate>Mon, 19 Feb 2024 05:17:26 GMT</pubDate>
            <description><![CDATA[<h3 id="전체-문제-보기"><a href="https://school.programmers.co.kr/learn/courses/30/lessons/42888_">전체 문제 보기</a></h3>
<blockquote>
</blockquote>
<h4 id="문제">문제</h4>
<p>카카오톡 오픈채팅방에서는 친구가 아닌 사람들과 대화를 할 수 있는데, 본래 닉네임이 아닌 가상의 닉네임을 사용하여 채팅방에 들어갈 수 있다.</p>
<blockquote>
</blockquote>
<p>신입사원인 김크루는 카카오톡 오픈 채팅방을 개설한 사람을 위해, 다양한 사람들이 들어오고, 나가는 것을 지켜볼 수 있는 관리자창을 만들기로 했다. 채팅방에 누군가 들어오면 다음 메시지가 출력된다.</p>
<blockquote>
</blockquote>
<p>&quot;[닉네임]님이 들어왔습니다.&quot;</p>
<blockquote>
</blockquote>
<p>채팅방에서 누군가 나가면 다음 메시지가 출력된다.</p>
<blockquote>
</blockquote>
<p>&quot;[닉네임]님이 나갔습니다.&quot;</p>
<blockquote>
</blockquote>
<p>채팅방에 들어오고 나가거나, 닉네임을 변경한 기록이 담긴 문자열 배열 record가 매개변수로 주어질 때, 모든 기록이 처리된 후, <strong>최종적으로 방을 개설한 사람이 보게 되는 메시지</strong>를 문자열 배열 형태로 return 하도록 solution 함수를 완성하라.</p>
<blockquote>
</blockquote>
<h4 id="입출력-예시">입출력 예시</h4>
<blockquote>
</blockquote>
<table>
<thead>
<tr>
<th>record</th>
<th>result</th>
</tr>
</thead>
<tbody><tr>
<td>[&quot;Enter uid1234 Muzi&quot;, &quot;Enter uid4567 Prodo&quot;,&quot;Leave uid1234&quot;,&quot;Enter uid1234 Prodo&quot;,&quot;Change uid4567 Ryan&quot;]</td>
<td>[&quot;Prodo님이 들어왔습니다.&quot;, &quot;Ryan님이 들어왔습니다.&quot;, &quot;Prodo님이 나갔습니다.&quot;, &quot;Prodo님이 들어왔습니다.&quot;]</td>
</tr>
</tbody></table>
<h3 id="풀이">풀이</h3>
<ol>
<li>user의 닉네임은 중복될 수 있기 때문에 딕셔너리 형태<strong><code>{user_id : user_name}</code></strong>로 저장하여 고유의 키값을 가지게 만든다.</li>
<li>user의 동작을 따로 저장하여 그에 맞게 출력을 한다.</li>
</ol>
<pre><code class="language-python">def solution(record):
    func_list = [] # user의 동작을 저장하는 리스트
    user_dict = {} # user의 정보를 저장하는 딕셔너리
    result = [] # 정답 출력을 저장하는 리스트

    for i in record:
    # Leave의 경우 split 결과가 2이기 때문에 if문으로 조건 지정
        parts = i.split(&#39; &#39;) # 공백을 기준으로 나뉘어 있기 때문에 split 사용
        if len(parts) == 3:
            func, id, name = parts
            func_list.append([func, id]) # 동작과 id를 함께 저장
            user_dict[id] = name
        elif len(parts) == 2:
            func, id = parts
            func_list.append([func, id])

    for cmd in func_list:
        if cmd[0] == &quot;Enter&quot;:
            result.append(f&quot;{user_dict[cmd[1]]}님이 들어왔습니다.&quot;)
        elif cmd[0] == &quot;Leave&quot;:
            result.append(f&quot;{user_dict[cmd[1]]}님이 나갔습니다.&quot;)
        else:
            continue # &quot;Change&quot;의 경우 따로 출력할 것이 없기 때문에 생략

    return result</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Algorithm]해시 (Hash)]]></title>
            <link>https://velog.io/@kyeonghun_kim/Algorithm%ED%95%B4%EC%8B%9C-Hash</link>
            <guid>https://velog.io/@kyeonghun_kim/Algorithm%ED%95%B4%EC%8B%9C-Hash</guid>
            <pubDate>Fri, 16 Feb 2024 06:06:21 GMT</pubDate>
            <description><![CDATA[<hr>
<h3 id="✏️-해시란">✏️ 해시란?</h3>
<p>&ensp;<strong>해시</strong>란 임의의 데이터를 변환 함수를 사용해 <strong>고정된 크기의 데이터</strong>로 변환한 값을 의미한다. 특정한 값을 입력받으면 길이에 상관없이 <strong>항상 일정한 결과</strong>를 만들어내는 것을 말한다.</p>
<p>&ensp;이러한 특성 때문에 값을 알고 있으면 어떤 결과를 알 수 있으며 정렬을 하지 않고 빠른 검색, 빠른 삽입을 할 수 있다. <strong>파이썬에서는 딕셔너리(dictionary) 자료형</strong>이 해시에 해당한다.</p>
<h4 id="✍🏻-해시-테이블hash-table">✍🏻 해시 테이블(Hash Table)</h4>
<p>&ensp;해싱(hasing)을 사용하여 데이터를 테이블 형태로 저장하는 구조를 해시 테이블이라고 한다. 파이썬의 경우 선언한 Key가 해시 함수를 통해 특정 값으로 변환되어 내부적으로 관리되고, 그에 대응하는 데이터가 연결되는 자료구조를 말한다.</p>
<p>&ensp;파이썬의 딕셔너리는 key와 value를 인자로 가지며, 선언할 때는 <code>{key: value}</code>의 형태로 선언한다.</p>
<pre><code class="language-python">fruit = {&#39;apple&#39;: 3, &#39;banana&#39;: 2, &#39;cherry&#39;: 7}
fruit[&#39;apple&#39;]
&gt;&gt;&gt; 3</code></pre>
<p>value를 호출할 때에는 리스트와 마찬가지로 대괄호를 사용하고 찾는 키를 직접 넣으면 된다. 단, 선언할 때의 제약이 있는데 Key는 문자열, 숫자, Boolean 형식만 사용이 가능하다. value의 경우에는 자료형의 형식에 구애받지 않으며 lambda 함수식도 지정할 수 있다.</p>
<h4 id="✍🏻-해시의-시간복잡도">✍🏻 해시의 시간복잡도</h4>
<p>&ensp;해시의 경우 시간복잡도 측면에서 매우 뛰어난 점을 보여준다. 탐색, 정렬에 걸리는 시간 비용이 크게 줄어든다. 배열의 경우 for문으로 순차 탐색을 시도하기 때문에 $$O(n)$$의 실행 시간을 가지지만 딕셔너리의 경우 특별한 경우가 아니라면 <strong>$$O(1)$$의 시간</strong>으로 원하는 요소를 찾을 수 있다.</p>
<p>&ensp;앞서 말한 특별한 경우는 어떤 것이 있을까? 딕셔너리에 사용되는 키는 해시 함수를 기반으로 하는데 낮은 확률로 동일한 값이 나올 수 있다. 이것을 <strong>충돌</strong>이라고 하며 이 충돌을 해결하는 방법은 <strong>개별 연결</strong>과 <strong>공개 주소</strong> 방식으로 나뉜다. <strong>파이썬에서는 공개 주소(Open Addressing) 방식</strong>을 사용한다.</p>
<blockquote>
</blockquote>
<ul>
<li><strong>개별 연결</strong>: 동일한 해시가 생성됐을 경우, 연결 리스트를 만들고 데이터를 집어넣어 한 해시에 연속적으로 연결된 형태를 취하는 방식. 만들기 쉽고 직관적이라는 장점이 있으나 연결이 많아지면 사실상 2차원 배열이 되는 단점이 있음.<blockquote>
</blockquote>
</li>
<li><strong>공개 주소</strong>: 인근의 빈 공간을 검색하여 값을 넣는 방식, 충돌시 주어진 계산식에 따라 위치를 다시 계산하여 배정함. 이미 생성된 리스트 내에서 해결하므로 추가적인 메모리를 차지하지 않지만 &#39;재충돌 현상&#39;이 발생하기 쉬운 단점이 있음.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Kaggle]타이타닉 데이터 분석(Titanic - Machine Learning from Disaster) - 2]]></title>
            <link>https://velog.io/@kyeonghun_kim/Kaggle%ED%83%80%EC%9D%B4%ED%83%80%EB%8B%89-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B6%84%EC%84%9DTitanic-Machine-Learning-from-Disaster-2</link>
            <guid>https://velog.io/@kyeonghun_kim/Kaggle%ED%83%80%EC%9D%B4%ED%83%80%EB%8B%89-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B6%84%EC%84%9DTitanic-Machine-Learning-from-Disaster-2</guid>
            <pubDate>Tue, 06 Feb 2024 03:21:04 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>&ensp;지난 시간에는 Datset을 처리하는 과정을 진행했다. 이제 학습 모델을 선정하고 학습을 시킨 후 최종 제출을 해보자!</p>
</blockquote>
<p>📌 이전 글
<a href="https://velog.io/@kyeonghun_kim/Kaggle%ED%83%80%EC%9D%B4%ED%83%80%EB%8B%89-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B6%84%EC%84%9DTitanic-Machine-Learning-from-Disaster-1">[Kaggle]타이타닉 데이터 분석(Titanic - Machine Learning from Disaster) - 1</a></p>
<h3 id="✍🏻-train-and-prediction">✍🏻 Train and Prediction</h3>
<h4 id="dataset-split">Dataset Split</h4>
<p>&ensp; 제공된 데이터셋은 train 데이터와 test 데이터가 나뉘어져 있지만 검증을 위해 valid 데이터를 추가하도록 하자.</p>
<pre><code class="language-python">from sklearn.model_selection import train_test_split
X_train = train_df.drop(&quot;Survived&quot;, axis = 1)
y_train = train_df[&quot;Survived&quot;]

X_train, X_val, y_train, y_val = train_test_split(X_train, y_train, test_size = 0.2, random_state = 77)

X_train.shape, X_val.shape, y_train.shape, y_val.shape
&gt;&gt;&gt;
((712, 6), (179, 6), (712,), (179,))</code></pre>
<h4 id="모델-선정하기">모델 선정하기</h4>
<p>&ensp; 목표인 &quot;Survived&quot; Column은 0과 1로 표현되기 때문에 이진 분류 문제라고 할 수 있다. 그렇기 때문에 Tree 기반의 모델과, Naive Bayes 모델을 선택하여 학습을 진행해보자.</p>
<pre><code class="language-python">from sklearn.model_selection import GridSearchCV
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.naive_bayes import GaussianNB
from lightgbm import LGBMClassifier
from xgboost import XGBClassifier
from sklearn.metrics import accuracy_score</code></pre>
<h4 id="모델-학습-및-예측-결과">모델 학습 및 예측 결과</h4>
<p>&ensp; 선택한 모델에 처리한 데이터를 학습시키고 예측 결과를 반환하도록 하자. 분류 문제의 경우 Accuracy를 많이 사용하기 때문에 성능 평가에 이를 반영한다.</p>
<pre><code class="language-python"># RandomForest Prediction
rf_clf = RandomForestClassifier()
rf_clf.fit(X_train, y_train)
rf_pred = rf_clf.predict(X_val)
print(f&#39;{accuracy_score(y_val, rf_pred):.2f}&#39;)
&gt;&gt;&gt;
0.85</code></pre>
<pre><code class="language-python"># Decision Tree Prediction
dt_clf = DecisionTreeClassifier()
dt_clf.fit(X_train, y_train)
dt_pred = dt_clf.predict(X_val)
print(f&#39;{accuracy_score(y_val, dt_pred):.2f}&#39;)
&gt;&gt;&gt;
0.84</code></pre>
<pre><code class="language-python"># Gaussian NB Prediction
nb_clf = GaussianNB()
nb_clf.fit(X_train, y_train)
nb_pred = nb_clf.predict(X_val)
print(f&#39;{accuracy_score(y_val, nb_pred):.2f}&#39;)
&gt;&gt;&gt;
0.75</code></pre>
<p>&ensp; 하이퍼 파라미터를 조정하지 않고 예측을 한 결과 RandomForest 모델이 가장 좋은 결과를 내고 있었다. GirdSearchCV를 사용해서 최적의 파라미터를 찾아보도록 하자.</p>
<pre><code class="language-python">param_rf = {&quot;n_estimators&quot; : [50, 100, 200, 300], &quot;max_depth&quot;: [10, 20, 30, 50]}
rf_clf = RandomForestClassifier()
grs_rf = GridSearchCV(rf_clf, param_grid = param_rf, scoring = &#39;accuracy&#39;, cv = 2)
grs_rf.fit(X_train, y_train)
print(grs_rf.best_estimator_)
&gt;&gt;&gt;
RandomForestClassifier(max_depth=10, n_estimators=200)</code></pre>
<h4 id="결과-제출">결과 제출</h4>
<pre><code class="language-python">test = test_df.copy()
test_pred = rf_clf.predict(test)

result = pd.DataFrame({&quot;PassengerId&quot;: test_id, &quot;Survived&quot;: test_pred})
result.to_csv(&quot;/content/drive/MyDrive/kaggle/titanic/submission.csv&quot;, index = False)</code></pre>
<p>&ensp; 해당 결과물을 제출한 결과 Score가 0.76555로 12,200번째 순위에 들었다. 더 개선할 수 있는 점을 찾아보자.</p>
<h3 id="✍🏻-모델-재선정-및-결과-제출">✍🏻 모델 재선정 및 결과 제출</h3>
<p>&ensp; 모델을 개선시킬 수 있는 방법은 데이터를 다시 뜯어보거나, 다른 모델을 사용해보는 등의 방법이 있다. Competition LeaderBoard를 살펴보니 다른 사람들이 경사하강법(Gradient Descent)을 적용한 모델을 사용한 것을 발견했다.</p>
<pre><code class="language-python">params = {&quot;n_estimators&quot; : [50, 100, 200, 300], &quot;max_depth&quot;: [10, 20, 30, 50], &quot;learning_rate&quot;: [0.001, 0.003, 0.005, 0.01]}
model = GradientBoostingClassifier()
grid_model = GridSearchCV(model, param_grid = params, scoring = &#39;accuracy&#39;)
grid_model.fit(X_train, y_train)

print(grid_model.best_score_)
print(grid_model.best_params_)
&gt;&gt;&gt;
0.8019994090416626
{&#39;learning_rate&#39;: 0.003, &#39;max_depth&#39;: 10, &#39;n_estimators&#39;: 200}</code></pre>
<p>&ensp; 해당 모델을 저장하여 결과를 제출한 결과 Score가 0.7799로 전체 15,995 팀 중 3,801번째가 될 수 있었다.</p>
<p><img src="https://velog.velcdn.com/images/kyeonghun_kim/post/7c0fa4fc-57fb-441f-8761-5e2e6787a949/image.png" alt=""></p>
<hr>
<p>© 참고</p>
<p><a href="https://www.kaggle.com/code/startupsci/titanic-data-science-solutions">Titanic Data Science Solutions</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Kaggle]타이타닉 데이터 분석(Titanic - Machine Learning from Disaster) - 1]]></title>
            <link>https://velog.io/@kyeonghun_kim/Kaggle%ED%83%80%EC%9D%B4%ED%83%80%EB%8B%89-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B6%84%EC%84%9DTitanic-Machine-Learning-from-Disaster-1</link>
            <guid>https://velog.io/@kyeonghun_kim/Kaggle%ED%83%80%EC%9D%B4%ED%83%80%EB%8B%89-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B6%84%EC%84%9DTitanic-Machine-Learning-from-Disaster-1</guid>
            <pubDate>Mon, 05 Feb 2024 07:47:53 GMT</pubDate>
            <description><![CDATA[<h3 id="💡-intro">💡 Intro</h3>
<p>&ensp; Kaggle에서 가장 유명한 Competition를 꼽으라면 <code>Titanic - Machine Learning from Disaster</code>를 말할 것이다. ML 학습의 바이블, Titanic Dataset을 분석해보자!</p>
<h3 id="✍🏻-import-library--data-load">✍🏻 Import Library &amp; Data Load</h3>
<p>&ensp; Kaggle에 업로드 된 Tatanic Dataset을 활용하여 분석을 시작한다. train과 test가 나뉘어져 있으며 sample Submission도 제공하고 있다.</p>
<pre><code class="language-python"># 필요한 라이브러리 설치, 데이터 확인, 시각화에 필요한 것 설치
import pandas as pd
import numpy as np
import seaborn as sns
sns.set_palette(&quot;rocket_r&quot;) # seaborn basic palette setting
import matplotlib.pyplot as plt</code></pre>
<pre><code class="language-python"># train, test 데이터 check
train_df = pd.read_csv(&quot;/content/drive/MyDrive/kaggle/titanic/train.csv&quot;)
test_df = pd.read_csv(&quot;/content/drive/MyDrive/kaggle/titanic/test.csv&quot;)

print(&#39;Shape of Train Data:&#39;, train_df.shape)
print(&#39;Shape of Test Data:&#39;, test_df.shape)

&gt;&gt;&gt; Shape of Train Data: (891, 12)
    Shape of Test Data: (418, 11)</code></pre>
<h3 id="✍🏻-data-check">✍🏻 Data Check!</h3>
<h4 id="train-data-확인">train data 확인</h4>
<pre><code class="language-python">train_df.info()

&gt;&gt;&gt;
&lt;class &#39;pandas.core.frame.DataFrame&#39;&gt;
RangeIndex: 891 entries, 0 to 890
Data columns (total 12 columns):
 #   Column       Non-Null Count  Dtype  
---  ------       --------------  -----  
 0   PassengerId  891 non-null    int64  
 1   Survived     891 non-null    int64  
 2   Pclass       891 non-null    int64  
 3   Name         891 non-null    object 
 4   Sex          891 non-null    object 
 5   Age          714 non-null    float64
 6   SibSp        891 non-null    int64  
 7   Parch        891 non-null    int64  
 8   Ticket       891 non-null    object 
 9   Fare         891 non-null    float64
 10  Cabin        204 non-null    object 
 11  Embarked     889 non-null    object 
dtypes: float64(2), int64(5), object(5)
memory usage: 83.7+ KB</code></pre>
<p>&ensp; 대부분 Column에 결측치가 없지만 일부 Column(&quot;Cabin&quot;, &quot;Age&quot;)에서 결측치가 많이 존재함.</p>
<h4 id="columns-설명">Columns 설명</h4>
<p>&ensp; Kaggle에서 제공하고 있는 Column의 설명은 다음과 같다.</p>
<blockquote>
</blockquote>
<table>
<thead>
<tr>
<th>Column</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>Survived</td>
<td><strong>Target Columns</strong>, 예측해야할 값</td>
</tr>
<tr>
<td>Pclass</td>
<td><strong>선실의 등급(1등석, 2등석, 3등석)</strong>}</td>
</tr>
<tr>
<td>Name</td>
<td>탑승자의 <strong>이름</strong></td>
</tr>
<tr>
<td>Sex</td>
<td>탑승자의 <strong>성별</strong></td>
</tr>
<tr>
<td>Age</td>
<td>탑승자의 <strong>나이</strong></td>
</tr>
<tr>
<td>SibSp</td>
<td>탑승자의 <strong>형제/자매, 배우자 수</strong></td>
</tr>
<tr>
<td>Parch</td>
<td>탑승자의 <strong>부모 or 자식의 수</strong></td>
</tr>
<tr>
<td>Fare</td>
<td><strong>승객 운임</strong></td>
</tr>
<tr>
<td>Cabin</td>
<td><strong>선실 이름</strong></td>
</tr>
<tr>
<td>Embarked</td>
<td><strong>탑승 항구</strong> (C, Q, S)</td>
</tr>
</tbody></table>
<h4 id="column-check">Column Check</h4>
<p>&ensp; Column들을 확인해보자.</p>
<pre><code class="language-python"># Distribution of Columns(Class, Embarked, SibSp, Parch)
fig, ax = plt.subplots(ncols = 2, nrows = 2, constrained_layout=True, figsize = (8, 6))

sns.countplot(data = train_df, x = &quot;Pclass&quot;, ax = ax[0, 0])
ax[0, 0].set_title(&quot;Distribution of Class&quot;)
sns.countplot(data = train_df, x = &quot;Embarked&quot;, ax= ax[0, 1])
ax[0, 1].set_title(&quot;Distribution of Port&quot;)

sns.countplot(data = train_df, x = &quot;SibSp&quot;, ax = ax[1, 0])
ax[1, 0].set_title(&quot;Distribution of SibSp&quot;)

sns.countplot(data = train_df, x = &quot;Parch&quot;, ax = ax[1, 1])
ax[1, 1].set_title(&quot;Distribution of Parch&quot;)

plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/kyeonghun_kim/post/b3c3864d-9aca-42fb-856c-e8d371695bfc/image.png" alt=""></p>
<pre><code class="language-python"># Check Age Column
train_df[&quot;Age&quot;].describe()

&gt;&gt;&gt;
---
count    714.000000
mean      29.699118
std       14.526497
min        0.420000
25%       20.125000
50%       28.000000
75%       38.000000
max       80.000000
Name: Age, dtype: float64
---</code></pre>
<pre><code class="language-python"># histplot of Age Columns

plt.figure(figsize = (12, 6))
sns.histplot(data = train_df, x = train_df[&quot;Age&quot;], kde=True)
plt.title(&quot;Distribution of Age&quot;)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/kyeonghun_kim/post/7559dd96-3af5-4ba4-8580-c0cf95d89ca5/image.png" alt=""></p>
<pre><code class="language-python"># Survival Count by Age Group
age_survival_data = train_df[[&#39;Age&#39;, &#39;Survived&#39;]].dropna()

bins = [0, 10, 20, 30, 40, 50, 60, 70, 80]

age_survival_data[&#39;AgeGroup&#39;] = pd.cut(age_survival_data[&#39;Age&#39;], bins=bins, right=True)

plt.figure(figsize=(10, 6))
sns.countplot(data=age_survival_data, x=&#39;AgeGroup&#39;, hue=&#39;Survived&#39;)
plt.title(&#39;Survival Count by Age Group&#39;)
plt.xlabel(&#39;Age Group&#39;)
plt.ylabel(&#39;Count&#39;)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/kyeonghun_kim/post/0d90998c-ff9e-4645-9fa1-5be4ce8fcefc/image.png" alt=""></p>
<pre><code class="language-python"># Check Fare Column

train_df[&quot;Fare&quot;].describe()
&gt;&gt;&gt;
---
count    891.000000
mean      32.204208
std       49.693429
min        0.000000
25%        7.910400
50%       14.454200
75%       31.000000
max      512.329200
Name: Fare, dtype: float64
---</code></pre>
<pre><code class="language-python"># Distribution of Fare Column
plt.figure(figsize = (12, 6))
sns.histplot(train_df[&quot;Fare&quot;], kde = True)
plt.title(&quot;Distribution of Fare&quot;)
plt.xlabel(&quot;Fare Group&quot;)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/kyeonghun_kim/post/54ec6e9c-34bc-4c81-89bd-19936b46ff46/image.png" alt=""></p>
<pre><code class="language-python"># Survival Count by Fare Group
fare_data = train_df[[&quot;Fare&quot;, &quot;Survived&quot;]].dropna()
bins = [0, 50, 100, 150, 200, 250, 300, 350, 400, 450, 500] # 소득 분포 나누기
fare_data[&quot;FareGroup&quot;] = pd.cut(fare_data[&quot;Fare&quot;],
                        bins = bins, right = True) # cut()을 사용해서 &quot;FareGroup&quot; Column 생성

# Count by Fare Group
fig, ax = plt.subplots(nrows = 2, constrained_layout=True, figsize = (8, 8))
sns.countplot(data=fare_data, x=&quot;FareGroup&quot;, ax = ax[0])
ax[0].set_title(&quot;Count by Fare Group&quot;)
ax[0].set_xlabel(&quot;Fare Group&quot;)

# Survival Count by Fare Group
sns.countplot(data=fare_data, x=&#39;FareGroup&#39;, hue=&#39;Survived&#39;, ax = ax[1])
ax[1].set_title(&quot;Survival Count by Fare Group&quot;)
ax[1].set_xlabel(&quot;Fare Group&quot;)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/kyeonghun_kim/post/4756c299-855f-438b-b549-eb9aea8ebf22/image.png" alt=""></p>
<pre><code class="language-python"># Check Survived Columns

fig, ax = plt.subplots(ncols = 3, constrained_layout=True, figsize = (12, 4))
sns.countplot(data = train_df, x = train_df[&quot;Survived&quot;], hue = &quot;Sex&quot;, ax = ax[0])
ax[0].set_title(&quot;Survival Counts by Gender&quot;)

sns.countplot(data = train_df, x = train_df[&quot;Survived&quot;], hue = &quot;Pclass&quot;, ax = ax[1])
ax[1].set_title(&quot;Survival Counts by Class&quot;)

sns.countplot(data = train_df, x = train_df[&quot;Survived&quot;], hue = &quot;Embarked&quot;, ax = ax[2])
ax[2].set_title(&quot;Survival Counts by Port&quot;)

plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/kyeonghun_kim/post/41ac7c6c-763f-4066-a4e8-14082d528c4b/image.png" alt=""></p>
<pre><code class="language-python"># Check the Survival Rate

def getDataFrame(column1, column2, data):
    df = data[[column1, column2]].groupby([column1], as_index = False).mean().sort_values(by = column2, ascending = False)
    return df

cols = [&quot;Pclass&quot;, &quot;Sex&quot;, &quot;SibSp&quot;, &quot;Parch&quot;, &quot;Embarked&quot;]

fig, ax = plt.subplots(ncols = 5, constrained_layout=True, figsize = (12, 4))
for idx, column in enumerate(cols):
    df = getDataFrame(column, &quot;Survived&quot;, train_df)
    ax[idx].bar(df[column], df[&quot;Survived&quot;])
    ax[idx].set_title(f&quot;Survival Rate by {column}&quot;)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/kyeonghun_kim/post/f7ea587c-b203-4235-8162-bf20bb60f377/image.png" alt=""></p>
<h4 id="🔎-conclusion">🔎 Conclusion</h4>
<p>&ensp; <strong>&quot;SibSp&quot;</strong> Column은 대부분의 값이 0이고 <strong>&quot;Parch&quot;</strong>의 경우도 0이 대부분의 값을 차지하고 있는 것을 볼 수 있음. <strong>&quot;Embarked&quot;</strong>는 S값, <strong>&quot;Pclass&quot;</strong>는 3등석이 가장 많은 비율을 차지하고 있음.</p>
<p>&ensp; <strong>&quot;Age&quot;</strong> Column을 살펴본 결과, <strong>20-40대 사이의 나이</strong>가 가장 많이 생존하지 못한 것으로 파악됨.</p>
<p>&ensp; <strong>&quot;Fare&quot;</strong> Column의 분포는 오른쪽으로 길게 늘어진 형태를 보이고 있으나 일부 극단치가 존재하는 것을 볼 수 있음. 또한, <strong>0-50의 소득</strong>을 가지고 있는 사람이 가장 많이 생존하지 못한 것으로 파악됨.</p>
<p>&ensp; <strong>Target</strong>인 <strong>&quot;Survived&quot;</strong> Column에서는 <strong>남자</strong>일수록, <strong>3등석</strong>일수록 가장 많이 생존하지 못한 것으로 보이며, <strong>탑승 항구</strong>에 따라 차이가 보이는 것은 단순히 <strong>S항구</strong>에서 많은 사람이 탑승했기 때문인 것으로 추측됨.</p>
<h3 id="✍🏻-data-preprocessing">✍🏻 Data Preprocessing</h3>
<p>&ensp; 데이터 전처리를 시작하자. 결측치가 있다면 적절한 값으로 처리하고, 학습에 도움이 될 수 있는 새로운 Feature 생성 등을 수행하여 학습에 유용한 데이터로 만든다.</p>
<h4 id="check-missing-value">Check Missing Value</h4>
<pre><code class="language-python"># Check Missing Data
print(&quot;Train Data Missing Value&quot;)
print(train_df.isnull().sum(), &quot;\n&quot;, &quot;=&quot; * 20)
print(&quot;Test Data Missing Value&quot;)
print(test_df.isnull().sum())

&gt;&gt;&gt;
Train Data Missing Value
PassengerId      0
Survived         0
Pclass           0
Name             0
Sex              0
Age            177
SibSp            0
Parch            0
Ticket           0
Fare             0
Cabin          687
Embarked         2
dtype: int64 
 ====================
Test Data Missing Value
PassengerId      0
Pclass           0
Name             0
Sex              0
Age             86
SibSp            0
Parch            0
Ticket           0
Fare             1
Cabin          327
Embarked         0
dtype: int64</code></pre>
<h4 id="drop-column">Drop Column</h4>
<p>&ensp; Target Feature인 &quot;Survived&quot;와 관련이 없거나, 결측치가 많아 적절한 값으로 처리할 수 없는 Column을 삭제한다.</p>
<p>&ensp; &quot;Ticket&quot;, &quot;PassengerId&quot;, &quot;Name&quot; Column은 &quot;Survived&quot;과 유의미한 관계를 가지지 못하기 때문에 삭제한다.</p>
<p>&ensp; &quot;Cabin&quot; Column은 결측치가 많이 존재하기 때문에 삭제한다.</p>
<pre><code class="language-python"># Drop Columns
print(&quot;Before&quot;, train_df.shape, test_df.shape)
test_id = test_df[&quot;PassengerId&quot;] # submission을 위해 따로 저장.

train_df = train_df.drop([&quot;Name&quot;, &quot;PassengerId&quot;, &quot;Cabin&quot;, &quot;Ticket&quot;], axis = 1)
test_df = test_df.drop([&quot;Name&quot;, &quot;PassengerId&quot;, &quot;Cabin&quot;, &quot;Ticket&quot;], axis = 1)
all_dataset = [train_df, test_df] # 동일한 처리를 위해 합쳐둠.

print(&quot;After&quot;, train_df.shape, test_df.shape)

&gt;&gt;&gt;
Before (891, 12) (418, 11)
After (891, 8) (418, 7)</code></pre>
<h4 id="age-column-float-value-check">Age Column float value check</h4>
<p>&ensp; &quot;Age&quot; Column은 <code>float</code> 값으로 채워져있다. 살펴본 결과 0.83세, 0.67세, 25.50세 등이 있는 것으로 확인됐다. 이러한 값을을 정수형으로 처리하자.</p>
<pre><code class="language-python"># check float value based on 0.5
import math
# 0.5 이상 값 확인
under_value = train_df.loc[train_df[&quot;Age&quot;].apply(lambda x: math.modf(x)[0]) &gt;= 0.5]

# 0.5 미만 값 확인
over_value = train_df.loc[train_df[&quot;Age&quot;].apply(lambda x: math.modf(x)[0]) &lt; 0.5]
print(&#39;Value of Under 0.5 :&#39;, under_value.shape[0])
print(&#39;Value of Under 0.5 :&#39;, over_value.shape[0])

&gt;&gt;&gt;
Value of Under 0.5 : 24
Value of Under 0.5 : 867</code></pre>
<p>&ensp; 0.5 값 이상인 경우는 24개, 0.5 값 미만인 경우는 데이터의 대부분을 차지하고 있는 것을 확인했다. 외국인들의 나이이기 때문에 소수점 부분을 모두 버리는 것으로 결정한다.</p>
<pre><code class="language-python"># Convert the &#39;Age&#39; column from float to int

for data in all_dataset:
    data[&quot;Age&quot;] = data[&quot;Age&quot;].astype(int)

print(&#39;Data type of Age Column in Train Dataset :&#39;, train_df[&quot;Age&quot;].dtype)
print(&#39;Data type of Age Column in Train Dataset :&#39;, test_df[&quot;Age&quot;].dtype)

&gt;&gt;&gt;
Data type of Age Column in Train Dataset : int64
Data type of Age Column in Test Dataset : int64</code></pre>
<h4 id="create-new-feature">Create New Feature</h4>
<p>&ensp; Target Column과 관련된 새로운 Feature들을 만들자.</p>
<pre><code class="language-python"># Dividing into Age Segments Based on the Age Column
train_df[&#39;AgeBand&#39;] = pd.cut(train_df[&#39;Age&#39;], 5) # 5구간으로 나누기
train_df[[&#39;AgeBand&#39;, &#39;Survived&#39;]].groupby([&#39;AgeBand&#39;], as_index=False).mean().sort_values(by=&#39;AgeBand&#39;, ascending=True) # 구간별 생존률 확인

# Mapping values to the &#39;AgeBand&#39; column.
for data in all_dataset:
    data.loc[data[&quot;Age&quot;] &lt;= 16, &#39;Age&#39;] = 0
    data.loc[(data[&quot;Age&quot;] &gt; 16) &amp; (data[&quot;Age&quot;] &lt;= 32), &#39;Age&#39;] = 1
    data.loc[(data[&quot;Age&quot;] &gt; 32) &amp; (data[&quot;Age&quot;] &lt;= 48), &#39;Age&#39;] = 2
    data.loc[(data[&quot;Age&quot;] &gt; 48) &amp; (data[&quot;Age&quot;] &lt;= 64), &#39;Age&#39;] = 3
    data.loc[(data[&quot;Age&quot;] &gt; 64), &#39;Age&#39;] = 4

train_df.head()</code></pre>
<p>&ensp; 나이를 구간별로 5개로 나누고 그에 따라 숫자를 매핑했다.</p>
<pre><code class="language-python"># Dividing into Income Segments Based on the Fare Column

train_df[&#39;FareBand&#39;] = pd.qcut(train_df[&#39;Fare&#39;], 4) # 상대적인 소득 구분을 위해서 qcut() 사용
train_df[[&#39;FareBand&#39;, &#39;Survived&#39;]].groupby([&#39;FareBand&#39;], as_index=False).mean().sort_values(by=&#39;FareBand&#39;, ascending=True)

# Mapping values to the &#39;FareBand&#39; column.

for data in all_dataset:
    data.loc[data[&quot;Fare&quot;] &lt;= 7.91, &#39;Fare&#39;] = 0
    data.loc[(data[&quot;Fare&quot;] &gt; 7.91) &amp; (data[&quot;Fare&quot;] &lt;= 14.454), &#39;Fare&#39;] = 1
    data.loc[(data[&quot;Fare&quot;] &gt; 14.454) &amp; (data[&quot;Fare&quot;] &lt;= 31), &#39;Fare&#39;] = 2
    data.loc[data[&quot;Fare&quot;] &gt; 31, &#39;Fare&#39;] = 3
    data[&quot;Fare&quot;] = data[&quot;Fare&quot;].astype(int)

train_df.head()</code></pre>
<p>&ensp; 소득을 4개 구간으로 나누고 그에 따라 숫자를 매핑했다. 구간을 나눌 때는 cut()이 아니라 qcut()을 사용했는데, 소득의 경우 적은 소득을 가진 사람에게 분포가 치우쳐져 있기 때문이다.</p>
<pre><code class="language-python"># Create Family Size Column

for data in all_dataset:
    data[&quot;FamilySize&quot;] = data[&quot;SibSp&quot;] + data[&quot;Parch&quot;] + 1

train_df[[&#39;FamilySize&#39;, &#39;Survived&#39;]].groupby([&#39;FamilySize&#39;], as_index=False).mean().sort_values(by=&#39;Survived&#39;, ascending=False)

for data in all_dataset:
    data[&quot;IsAlone&quot;] = 0
    data.loc[data[&quot;FamilySize&quot;] == 1, &#39;IsAlone&#39;] = 1

train_df[[&#39;IsAlone&#39;, &#39;Survived&#39;]].groupby([&#39;IsAlone&#39;], as_index=False).mean()</code></pre>
<h3 id="마무리">마무리</h3>
<p>&ensp; 데이터를 살펴보고 그에 따라 전처리를 하면서 학습을 위한 데이터가 준비되었다. 다음 시간에는 여러 모델을 선택하여 학습시키고 예측하여 Submission을 제출하도록 하자.</p>
<hr>
<p>© 참고</p>
<p><a href="https://www.kaggle.com/code/startupsci/titanic-data-science-solutions">Titanic Data Science Solutions</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Kaggle]집값 예측하기(House Prices prediction) - 3]]></title>
            <link>https://velog.io/@kyeonghun_kim/Kaggle%EC%A7%91%EA%B0%92-%EC%98%88%EC%B8%A1%ED%95%98%EA%B8%B0House-Prices-prediction-3</link>
            <guid>https://velog.io/@kyeonghun_kim/Kaggle%EC%A7%91%EA%B0%92-%EC%98%88%EC%B8%A1%ED%95%98%EA%B8%B0House-Prices-prediction-3</guid>
            <pubDate>Wed, 31 Jan 2024 04:19:33 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>&ensp;지난 시간에는 Datset을 처리하는 과정을 진행했다. 이제 학습 모델을 선정하고 학습을 시킨 후 최종 제출을 해보자!</p>
</blockquote>
<p>📌 이전 글
<a href="https://velog.io/@kyeonghun_kim/Kaggle%EC%A7%91%EA%B0%92-%EC%98%88%EC%B8%A1%ED%95%98%EA%B8%B0House-Prices-prediction-1">[Kaggle]집값 예측하기(House Prices prediction) - 1</a>
<a href="https://velog.io/@kyeonghun_kim/Kaggle%EC%A7%91%EA%B0%92-%EC%98%88%EC%B8%A1%ED%95%98%EA%B8%B0House-Prices-prediction-2">[Kaggle]집값 예측하기(House Prices prediction) - 2</a></p>
<h3 id="✍🏻-split-data">✍🏻 Split Data</h3>
<p>&ensp; train Data와 test Data를 한꺼번에 처리하기 위해 all dataset을 만들었기 때문에 이것을 다시 split할 필요가 있고, 따로 Valid data를 만들어 학습을 검증하도록 하자.</p>
<p>&ensp; 결측치 처리 과정에서 일부 행을 삭제 했기 때문에 인덱스가 맞는 것끼리 매칭을 하여 train data의 SalePrice를 넣어줘야 한다.</p>
<pre><code class="language-python"># train, test split
train  = all_dataset.loc[all_dataset.index.isin(train_df.index)]
print(&#39;Train_data.shape :&#39;, train.shape)
test = all_dataset[len(train):]
print(&#39;Test_data.shape :&#39;, test.shape)</code></pre>
<pre><code class="language-python">train_df_sale_price = train_df[&quot;SalePrice&quot;] # train_df의 SalePrice를 따로 빼두고
train_df_sale_price

train = train.join(train_df_sale_price) # index를 기준으로 데이터프레임 합치기

# index 값 재설정
train.reset_index(drop = True, inplace = True)
test.reset_index(drop = True, inplace = True)

train.shape, test.shape</code></pre>
<h4 id="valid-data-split">Valid Data Split</h4>
<p>&ensp; 학습을 검증하기 위해 valid data를 따로 만든다. train data에서 20%를 valid data로 설정한다.</p>
<pre><code class="language-python">from sklearn.model_selection import train_test_split

X_train = train.drop([&#39;SalePrice&#39;], axis=1)
y_train = train[&#39;SalePrice&#39;]

X_train, X_val, y_train, y_val = train_test_split(X_train, y_train, test_size=0.2, shuffle=True)

print(&#39;X_train :&#39;, len(X_train))
print(&#39;X_val :&#39;, len(X_val))
print(&#39;y_train :&#39;, len(y_train))
print(&#39;y_val :&#39;, len(y_val))</code></pre>
<h3 id="✍🏻-모델-선정-및-학습">✍🏻 모델 선정 및 학습</h3>
<p>&ensp; 학습을 위한 데이터를 준비했으니 모델을 선정하여 학습시키자. xgboost와 Lasso 모델을 선택하여 학습을 시킨다.</p>
<pre><code class="language-python">from sklearn.model_selection import GridSearchCV
from sklearn.metrics import mean_squared_error
from sklearn.linear_model import Lasso
from xgboost import XGBRegressor

xgbreg = XGBRegressor()
Laleg = Lasso()

# GridSearchCV를 사용하기 위해 후보 파라미터 선정
param_xgb = {&#39;max_depth&#39; : [30, 20, 10], &#39;min_child_weight&#39; : [1, 3, 6, 10],
            &#39;n_estimators&#39; : [100, 200, 300], &#39;learning_rate&#39; : [0.001, 0.003, 0.005, 0.01]}

param_Laleg = {&#39;alpha&#39;: [0.001, 0.003, 0.005, 0.01]}

gscv_xgb = GridSearchCV(estimator = xgbreg, param_grid = param_xgb, scoring = &#39;neg_mean_squared_error&#39;, cv = 3, verbose = 2)
gscv_Laleg = GridSearchCV(estimator = Laleg, param_grid = param_Laleg, scoring = &#39;neg_mean_squared_error&#39;, cv = 3, verbose = 2)

gscv_xgb.fit(X_train, y_train)
gscv_Laleg.fit(X_train, y_train)</code></pre>
<pre><code class="language-python"># xgbreg 학습
xgbreg = XGBRegressor(learning_rate = 0.01, max_depth = 10, min_child_weight = 10, n_estimators = 300, eval_metric= mean_squared_error)
xgbreg.fit(X_train, y_train, eval_set = [(X_val, y_val)])
pred_val = xgbreg.predict(X_val)
mean_squared_error(y_val, pred_val)

&gt;&gt;&gt; 0.023253598358027695

# 결과 제출
pred_test = xgbreg.predict(test)

id_pred_df = pd.DataFrame()
id_pred_df[&#39;Id&#39;] = test_id
id_pred_df[&#39;SalePrice&#39;] = pred_test

id_pred_df.to_csv(&#39;/content/drive/MyDrive/kaggle/house-prices/submission_xgbReg.csv&#39;, index=False)</code></pre>
<h3 id="회고">회고</h3>
<ol>
<li>부동산 Dataset을 분석하는 데 있어서 각 column을 해석하는 데 있어 도메인 지식이 필요했다.</li>
<li>데이터를 전처리하는 과정에서 여러 고민할 점(결측치를 어떻게 처리할 것인지, 데이터의 분포를 어떻게 처리할 것인지 등)이 있었다.</li>
<li>리더보드 상위 코드를 일부 참조하여 제출한 결과이기 때문에 스스로 결과를 향상시킬 수 있는 방법을 고민해봐야겠다.</li>
</ol>
<hr>
<p>© 참고</p>
<p><a href="https://www.kaggle.com/code/serigne/stacked-regressions-top-4-on-leaderboard">Stacked Regressions : Top 4% on LeaderBoard</a>
<a href="https://velog.io/@jaehyeong/Kaggle-%EB%B3%B4%EC%8A%A4%ED%84%B4-%EC%A3%BC%ED%83%9D-%EA%B0%80%EA%B2%A9-%EC%98%88%EC%B8%A1House-Prices-Advanced-Regression-Techniques">[Kaggle] 보스턴 주택 가격 예측(House Prices: Advanced Regression Techniques)</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Kaggle]집값 예측하기(House Prices prediction) - 2]]></title>
            <link>https://velog.io/@kyeonghun_kim/Kaggle%EC%A7%91%EA%B0%92-%EC%98%88%EC%B8%A1%ED%95%98%EA%B8%B0House-Prices-prediction-2</link>
            <guid>https://velog.io/@kyeonghun_kim/Kaggle%EC%A7%91%EA%B0%92-%EC%98%88%EC%B8%A1%ED%95%98%EA%B8%B0House-Prices-prediction-2</guid>
            <pubDate>Tue, 30 Jan 2024 10:05:30 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>&ensp;지난 시간에는 Datset의 데이터를 확인했다. 계속 해보자!</p>
</blockquote>
<p>📌 이전 글
<a href="https://velog.io/@kyeonghun_kim/Kaggle%EC%A7%91%EA%B0%92-%EC%98%88%EC%B8%A1%ED%95%98%EA%B8%B0House-Prices-prediction-1">[Kaggle]집값 예측하기(House Prices prediction) - 1</a></p>
<h3 id="✍🏻-saleprice-check">✍🏻 SalePrice Check</h3>
<h4 id="데이터-분포-재확인">데이터 분포 재확인</h4>
<p>&ensp; 예측 목표인 SalePrice의 분포를 확인했을 때 분포가 살짝 왼쪽으로 치우쳐있는 것을 확인했다. 이러한 경우 왜도와 첨도를 줄여 정규 분포의 형태로 만들어주는 것이 필요하다.</p>
<pre><code class="language-python"># log transform
train_df[&quot;SalePrice&quot;] = np.log(train_df[&quot;SalePrice&quot;]) # 값이 10만 단위이기 때문에 log변환을 진행</code></pre>
<blockquote>
<h4 id="❓로그-변환log-transform">❓로그 변환(Log transform)</h4>
<p>&ensp; <strong>로그 변환</strong>이란 큰 수 x에 로그를 취해 작은 수로 만드는 것을 말한다. 분포가 치우쳐져 있을 때, 특히 <strong>왼쪽으로 치우쳐져 있을 때(Positive Skewness)일 때 사용하면 유용</strong>하다.</p>
</blockquote>
<p>&ensp; 하지만, <strong>데이터를 변경한 경우</strong>이기 때문에 해석에 주의해야 한다.</p>
<pre><code class="language-python"># &#39;SalePrice&#39; 분포 시각화 및 QQ-Plot
sns.histplot(train_df[&#39;SalePrice&#39;], kde = True, common_norm=True)

#Now plot the distribution
plt.legend([&#39;Normal dist. ($\mu=$ {:.2f} and $\sigma=$ {:.2f} )&#39;.format(mu, sigma)],
            loc=&#39;best&#39;)
plt.ylabel(&#39;Frequency&#39;)
plt.title(&#39;SalePrice distribution&#39;)

#Get also the QQ-plot -&gt; SalePrice와 정규분포 비교
fig = plt.figure()
res = stats.probplot(train_df[&#39;SalePrice&#39;], plot=plt)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/kyeonghun_kim/post/0aab9681-e49a-4ec4-bd98-b393c92e16a0/image.png" alt=""> | <img src="https://velog.velcdn.com/images/kyeonghun_kim/post/b9516b68-320d-474a-8b25-9ef673e7c455/image.png" alt="">
|---|---|</p>
<h3 id="✍🏻-check-selected-columns">✍🏻 Check Selected Columns</h3>
<p>&ensp; SalePrice와 관련이 있다고 생각했던 Columns들을 확인해보자. 각각 &#39;Neighborhood&#39;, &#39;OverallCond&#39;, &#39;YearBuilt&#39;, &#39;GrLivArea&#39;, &#39;TotalBsmtSF&#39; 컬럼을 확인할 것이다.</p>
<pre><code class="language-python"># Scatter Plot SalePrice and selected column(Numeric data)
plt.figure(figsize = (20, 6))

plt.subplot(1, 3, 1)
plt.title(&quot;Scatter Plot SalePrice and YearBuilt&quot;)
plt.scatter(x=train_df[&#39;YearBuilt&#39;], y=train_df[&#39;SalePrice&#39;])

plt.subplot(1, 3, 2)
plt.title(&quot;Scatter Plot SalePrice and GrLivArea&quot;)
plt.scatter(x=train_df[&#39;GrLivArea&#39;], y=train_df[&#39;SalePrice&#39;])

plt.subplot(1, 3, 3)
plt.title(&quot;Scatter Plot SalePrice and TotalBsmtSF&quot;)
plt.scatter(x=train_df[&#39;TotalBsmtSF&#39;], y=train_df[&#39;SalePrice&#39;])

plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/kyeonghun_kim/post/72373fe7-92c1-434e-a076-44ef56e92795/image.png" alt=""></p>
<pre><code class="language-python"># boxplot
plt.figure(figsize = (12, 12))
sns.boxplot(x=train_df[&quot;SalePrice&quot;], y=train_df[&#39;Neighborhood&#39;], 
                        palette=&quot;flare&quot;, hue = train_df[&quot;Neighborhood&quot;], legend = False)
plt.title(&quot;SalePrice by Neighborhood&quot;)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/kyeonghun_kim/post/8a9b9f29-2cc1-4019-a297-0a8835bb391c/image.png" alt=""></p>
<pre><code class="language-python">plt.figure(figsize = (12, 12))
sns.boxplot(x=train_df[&quot;OverallCond&quot;], y=train_df[&quot;SalePrice&quot;], 
                        palette=&quot;flare&quot;, hue = train_df[&quot;OverallCond&quot;], legend = False)
plt.title(&quot;SalePrice by OverallCond&quot;)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/kyeonghun_kim/post/f018c6bb-de91-45ea-abc5-af26d7bc4282/image.png" alt=""></p>
<h3 id="✍🏻-feature-engineering">✍🏻 Feature Engineering</h3>
<p>&ensp; 어떤 데이터를 처리할 때는 Test Dataset과 Train Dataset을 동일하게 처리해야 한다. 그러기 위해서 Test Data와 Train Data를 합쳐 All Dataset을 만든다.</p>
<pre><code class="language-python">train_num = train_df.shape[0]
test_num = test_df.shape[0]

all_dataset = pd.concat((train_df, test_df)).reset_index(drop=True)
all_dataset.drop([&quot;SalePrice&quot;], axis = 1, inplace = True) # Target Feature인 SalePrice는 제거

all_dataset.shape

&gt; output (2919, 79)</code></pre>
<h4 id="결측치-확인-및-처리">결측치 확인 및 처리</h4>
<p>&ensp; 데이터셋에서 NaN, None 등으로 표기되어 있는 곳들은 결측치이다. 이것들을 적절히 처리하지 않으면 학습에 영향을 줄 수 있기 때문에 처리해야 한다.</p>
<pre><code class="language-python">all_dataset_na = (all_dataset.isnull().sum() / len(all_dataset)) * 100 # Column별 결측치 비율 확인
all_dataset_na = all_dataset_na.drop(all_dataset_na[all_dataset_na == 0].index).sort_values(ascending = False) # 내림차순 정렬, 결측치가 없는 Column은 제외
missing_data = pd.DataFrame({&quot;MissingRatio&quot; : all_dataset_na})
missing_data</code></pre>
<pre><code class="language-python"># 결측치 비율 확인
plt.figure(figsize = (12, 6))
sns.barplot(x = missing_data.index, y = missing_data[&quot;MissingRatio&quot;],
            palette=&quot;flare&quot;, hue = missing_data.index, legend = False)
plt.title(&quot;Percentage of Mission Data by Feature&quot;)
plt.xticks(rotation = 90)
plt.xlabel(&quot;Features&quot;)
plt.ylabel(&quot;Percentage of Mission Data&quot;)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/kyeonghun_kim/post/7d012e7b-67e2-48e5-9c8b-6b72a494e230/image.png" alt=""></p>
<p>&ensp; 결측치를 처리하는 방법은 여러가지가 있으나 70% 이상의 결측치를 가지고 있는 Feature는 삭제, 0%에 가까운 결측치는 행을 삭제, 나머지 결측치는 &#39;None&#39; 값으로 대체한다.</p>
<pre><code class="language-python"># Delete Missing Ratio over 70%
drop_columns = list(missing_data.loc[missing_data[&quot;MissingRatio&quot;] &gt; 70].index)

all_dataset = all_dataset.drop(drop_columns, axis = 1)
all_dataset.shape</code></pre>
<pre><code class="language-python"># Delete Missing data under 5%, 컬럼 삭제가 아닌 행 삭제
drop_columns = list(missing_data.loc[missing_data[&quot;MissingRatio&quot;] &lt; 5].index)

for column in drop_columns:
    index = all_dataset.loc[all_dataset[column].isnull()].index
    all_dataset = all_dataset.drop(index, axis = 0) # 행 삭제</code></pre>
<pre><code class="language-python"># Impute Missing data
impute_columns = list(missing_data.loc[(missing_data[&quot;MissingRatio&quot;] &lt; 70) 
                        &amp; (missing_data[&quot;MissingRatio&quot;] &gt; 5)].index)

for column in impute_columns:
    if all_dataset[column].dtype == (&#39;float64&#39; or &#39;int64&#39;): # 수치형 자료의 경우 0으로 채워넣기
        all_dataset[column].fillna(0, inplace = True)
    else: # 범주형 자료의 경우 &#39;None&#39;으로 채워넣기
        all_dataset[column].fillna(&#39;None&#39;, inplace = True)</code></pre>
<h4 id="impute-과정에서의-문제점">Impute 과정에서의 문제점</h4>
<p>&ensp; 결측치가 존재하는 column을 확인한 결과 특정 값을 넣어줄 필요가 있음을 파악함. 예를 들어 Garage와 관련된 column의 경우 &#39;NA&#39;(No Garage) 값을 넣어야 함.</p>
<pre><code class="language-python"># Impute Missing data
impute_columns = list(missing_data.loc[(missing_data[&quot;MissingRatio&quot;] &lt; 70) &amp; (missing_data[&quot;MissingRatio&quot;] &gt; 5)].index)

# Grage data는 NaN 값을 &#39;NA&#39;로 처리
# FirePlaceQu는 NaN 값을 &#39;TA&#39;로 처리

for column in impute_columns:
    if column[:6] == &#39;Garage&#39;:
        all_dataset[column].fillna(&#39;NA&#39;, inplace = True)
    elif column[:4] == &#39;Fire&#39;:
        all_dataset[column].fillna(&#39;TA&#39;, inplace = True)
    else:
        all_dataset[column].fillna(0, inplace = True)</code></pre>
<h4 id="encoding">Encoding</h4>
<p>&ensp; 학습을 위해 Categorical Data를 Encoding한다. 그 전에 범주형 자료이지만 숫자로 표기된 Column들은 str type으로 데이터를 변경시킨다. 이후 Pandas의 get_dummies() 메서드를 사용한다.</p>
<pre><code class="language-python">#MSSubClass=The building class
all_dataset[&#39;MSSubClass&#39;] = all_dataset[&#39;MSSubClass&#39;].apply(str)

#Changing OverallCond into a categorical variable
all_dataset[&#39;OverallCond&#39;] = all_dataset[&#39;OverallCond&#39;].astype(str)

#Year and month sold are transformed into categorical features.
all_dataset[&#39;YrSold&#39;] = all_dataset[&#39;YrSold&#39;].astype(str)
all_dataset[&#39;MoSold&#39;] = all_dataset[&#39;MoSold&#39;].astype(str)</code></pre>
<pre><code class="language-python">cols = [column for column in all_dataset.columns if all_dataset[column].dtype == &#39;object&#39;] # get_dummies 메서드에 사용할 Columns

all_dataset = pd.get_dummies(all_dataset, columns = cols, drop_first = True)
all_dataset.shape</code></pre>
<hr>
<p>© 참고</p>
<p><a href="https://www.kaggle.com/code/serigne/stacked-regressions-top-4-on-leaderboard">Stacked Regressions : Top 4% on LeaderBoard</a>
<a href="https://velog.io/@jaehyeong/Kaggle-%EB%B3%B4%EC%8A%A4%ED%84%B4-%EC%A3%BC%ED%83%9D-%EA%B0%80%EA%B2%A9-%EC%98%88%EC%B8%A1House-Prices-Advanced-Regression-Techniques">[Kaggle] 보스턴 주택 가격 예측(House Prices: Advanced Regression Techniques)</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Kaggle]집값 예측하기(House Prices prediction) - 1]]></title>
            <link>https://velog.io/@kyeonghun_kim/Kaggle%EC%A7%91%EA%B0%92-%EC%98%88%EC%B8%A1%ED%95%98%EA%B8%B0House-Prices-prediction-1</link>
            <guid>https://velog.io/@kyeonghun_kim/Kaggle%EC%A7%91%EA%B0%92-%EC%98%88%EC%B8%A1%ED%95%98%EA%B8%B0House-Prices-prediction-1</guid>
            <pubDate>Thu, 25 Jan 2024 07:47:01 GMT</pubDate>
            <description><![CDATA[<h3 id="💡-intro">💡 Intro</h3>
<p>&ensp; 머신러닝을 공부하는 사람이라면, Kaggle을 알고 있는 사람이라면 한 번쯤은 <em><strong>&quot;House Prices - Advanced Regression Techniques&quot;</strong></em> Competition에 참가해 보았을 것이다. 이 Competition에서 제공하는 Dataset을 활용하여 Submission을 제출해 보자!</p>
<h3 id="✍🏻-import-library--data-load">✍🏻 Import Library &amp; Data Load</h3>
<p>&ensp; House-Prices dataset은 <strong>train data</strong>와 <strong>test data</strong>가 <strong>따로 제공</strong>되기 때문에 임의로 data를 split할 필요가 없다.</p>
<pre><code class="language-python"># 필요한 라이브러리 import
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns</code></pre>
<pre><code class="language-python"># train data와 test data 불러오기
train_df = pd.read_csv(&#39;train.csv&#39;)
test_df = pd.read_csv((&#39;test.csv&#39;))</code></pre>
<h3 id="✍🏻-data-확인하기">✍🏻 Data 확인하기</h3>
<p>&ensp; train 데이터를 확인해본 결과, 1460개의 데이터, 81개의 column이 있는 것을 확인함.</p>
<pre><code class="language-python">print(f&#39;train data의 갯수 : {len(train_df)}&#39;)
print(f&#39;train data의 column 갯수 : {len(train_df.columns)}&#39;)</code></pre>
<h4 id="문제점">문제점</h4>
<ol>
<li>column의 갯수가 81개로 매우 많음. 예측 목표인 SalePrice에 영향을 줄 수 있는 column을 선택할 필요가 있음.</li>
<li>column 설명을 확인한 결과, 부동산 도메인에 대한 지식이 필요함.</li>
</ol>
<h4 id="selected-columns">Selected Columns</h4>
<blockquote>
<p><em><strong>가설: 집의 가격에 영향을 미치는 것은 집의 크기, 위치, 상태일 것이다.</strong></em></p>
</blockquote>
<p>위의 가설에 따라 선택한 Column은 다음과 같다.</p>
<ul>
<li>Neighborhood: 집이 위치한 지역(번화가인지 아닌지를 평가할 수 있다고 생각함.)</li>
<li>OverallCond: 집의 전반적인 상태(0~10의 척도로 평가, 숫자가 높을수록 좋음.)</li>
<li>YearBuilt: 집이 지어진 시기(오래된 건물은 비교적 가격이 낮을 것임.)</li>
<li>GrLivArea: 집의 크기(생활할 수 있는 면적)</li>
</ul>
<h3 id="✍🏻-saleprice-check">✍🏻 SalePrice Check</h3>
<p>&ensp; 예측의 목표인 SalePrice를 확인해보자! pandas의 <code>describe()</code>를 통해 데이터의 정보를 알 수 있다.</p>
<pre><code class="language-python"># SalePrice Check
train_df[&#39;SalePrice&#39;].describe()</code></pre>
<pre><code>output

count      1460.000000
mean     180921.195890
std       79442.502883
min       34900.000000
25%      129975.000000
50%      163000.000000
75%      214000.000000
max      755000.000000
Name: SalePrice, dtype: float64</code></pre><h4 id="saleprice-분포-확인">SalePrice 분포 확인</h4>
<pre><code class="language-python">plt.figure(figsize = (12, 6))
sns.histplot(train_df[&#39;SalePrice&#39;])
plt.title(&#39;Distribution of SalePrice&#39;)
plt.xlabel(&#39;SalePrice&#39;)
plt.ylabel(&#39;Counts&#39;)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/kyeonghun_kim/post/e366fffb-216b-4380-8000-a8382f3f8062/image.png" alt=""></p>
<p>&ensp; SalePrice의 분포를 확인했으니 왜도와 첨도를 확인해 보자!</p>
<pre><code class="language-python"># skewness, kurtosis check

print(f&#39;Skewness of SalePrice : {train_df[&quot;SalePrice&quot;].skew(): .2f}&#39;)
print(f&#39;Kurtosis of SalePrice : {train_df[&quot;SalePrice&quot;].kurt(): .2f}&#39;)

Skewness of SalePrice :  1.88
Kurtosis of SalePrice :  6.54</code></pre>
<p>왜도는 1.88, 첨도는 6.54로 나타났다. SalePrice의 분포는 오른쪽으로 꼬리가 생기지만 비교적 대칭적이고 중앙에 데이터가 모여 있는 것으로 판단할 수 있다.</p>
<blockquote>
<h4 id="❓왜도skewness와-첨도kurtosis">❓왜도(Skewness)와 첨도(Kurtosis)</h4>
<p><strong>왜도</strong>: 분포의 비대칭성을 나타낸다. 정규 분포의 경우 왜도가 0인 경우이며 완벽한 대칭을 뜻한다. 오른쪽으로 긴 꼬리가 나타날 경우에는 양수(Positive Skewness)이며 왼쪽으로 긴 꼬리가 나타날 경우에는 음수(Negative Skewness)이다.</p>
</blockquote>
<p><strong>첨도</strong>: 분포의 뾰족함을 나타낸다. 정규 분포의 경우 첨도가 0인 경우이다. 첨도가 높을수록 데이터가 중앙에 모여있다는 뜻이며 극단값이 많을 수 있다. 첨도가 낮으면 데이터가 중앙값 주위에 퍼져있다는 뜻이다.</p>
<hr>
<p>© 참고</p>
<p><a href="https://www.kaggle.com/code/serigne/stacked-regressions-top-4-on-leaderboard">Stacked Regressions : Top 4% on LeaderBoard</a>
<a href="https://velog.io/@jaehyeong/Kaggle-%EB%B3%B4%EC%8A%A4%ED%84%B4-%EC%A3%BC%ED%83%9D-%EA%B0%80%EA%B2%A9-%EC%98%88%EC%B8%A1House-Prices-Advanced-Regression-Techniques">[Kaggle] 보스턴 주택 가격 예측(House Prices: Advanced Regression Techniques)</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[데이터분석]A/B Test 알아보기 - 3]]></title>
            <link>https://velog.io/@kyeonghun_kim/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B6%84%EC%84%9DAB-Test-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0-3</link>
            <guid>https://velog.io/@kyeonghun_kim/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B6%84%EC%84%9DAB-Test-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0-3</guid>
            <pubDate>Tue, 23 Jan 2024 10:06:12 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>&ensp;지난 시간에는 A/B Test를 어떻게 하는지 알아보았다. 이번 시간에는 A/B 테스트를 <strong>어떻게 신뢰</strong>할 것인지, <strong>주의사항</strong>은 무엇인지 알아보자.</p>
</blockquote>
<p>📌 _이전 글
<a href="https://velog.io/@kyeonghun_kim/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B6%84%EC%84%9DAB-Test-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0-1">[데이터분석]A/B Test 알아보기 -1</a>
<a href="https://velog.io/@kyeonghun_kim/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B6%84%EC%84%9DAB-Test-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0-2">[데이터분석]A/B Test 알아보기 -2</a></p>
<h3 id="🤷🏻-ab-테스트-믿을-수-있을까">🤷🏻 A/B 테스트 믿을 수 있을까?</h3>
<p>&ensp; A/B 테스트가 종료되고 결과값을 받았을 때 의사결정자는 이 테스트를 어떻게 판단할 수 있을까? 가장 많이 활용되는 방식은 <strong>P-value</strong>가 있다. 통계 분석에서 P-value는 가장 널리 사용되는 것으로 A/B 테스트에도 유용하게 사용될 수 있다.</p>
<blockquote>
<p>&ensp; <strong>유의 확률(P-Value)</strong>이란 귀무가설이 맞다고 가정할 때 얻은 결과보다 극단적인 결과가 실제로 관측될 확률이다. 실험의 유의확률은 실험의 표본 공간에서 정의되는 확률변수로서, 0~1 사이의 값이다. - 위키백과</p>
</blockquote>
<p>&ensp; 단순히, 유의 확률은 <strong><em>&quot;어떤 사건이 우연히 발생할 확률&quot;</em></strong>을 말하며 이 값이 작을수록 이 사건이 우연히 발생할 확률이 낮기 때문에 인과관계가 있는 것으로 추정할 수 있는 것입니다.</p>
<h3 id="🤦🏻♂️-ab-테스트-시-주의할-점">🤦🏻‍♂️ A/B 테스트 시 주의할 점</h3>
<p>&ensp; 가장 널리 사용되고 뛰어난 효과를 가지고 있는 A/B Test도 만능은 아니다. 주의사항과 단점은 과연 무엇이 있을까?</p>
<h4 id="✔️-u전체-비즈니스-목표와-연결-지을-수-있어야-한다u">✔️ <u>전체 비즈니스 목표와 연결 지을 수 있어야 한다.</u></h4>
<p>&ensp; 단편적인 부분에 매몰된 나머지 <strong>전체 목표를 보지 못하는 오류</strong>를 범하는 것을 피하도록 주의해야 한다. 즉, 특정 부분에서 긍정적인 결과를 얻었다면 단기적으로는 제품 및 서비스 지표가 상승하는 듯 보일 수 있지만 최종적인 목표 달성에 미치는 영향은 미미할 수 있다.</p>
<p>&ensp; 이를 위해서는 제품 및 서비스에 대한 이해를 바탕으로 유의미한 수치를 얻어 <strong>비즈니스 KPI를 올리는 동시</strong>에, 사용자 경험 <strong>전반에 걸쳐 긍정적인 영향</strong>을 줄 수 있어야 한다.</p>
<h4 id="✔️-u테스트를-많이자주하면-단기적으로-손해가-발생할-수-있다u">✔️ <u>테스트를 많이/자주하면 단기적으로 손해가 발생할 수 있다.</u></h4>
<p>&ensp; 예를 들어, 쇼핑몰에서 구매전환율이 높은 상품 이미지가 무엇인지 알아보기 위한 테스팅을 진행한다고 치자. 2주 동안 전체 방문자를 50:50으로 나눠 기존 상품 이미지와 새로운 상품 이미지를 보여주고자 한다.</p>
<p>&ensp; 테스트를 3일 쯤 진행했더니 새로운 이미지를 본 집단에서의 매출이 기존에 비해 절반 밖에 나오지 않는다면 어떻게 해야하나? 애초에 계획했던 2주동안 테스트를 진행하려면 막심한 매출 손해를 감수해야한다.</p>
<h4 id="✔️-u시간의-흐름에-따라-바뀔-수-있다u">✔️ <u>시간의 흐름에 따라 바뀔 수 있다.</u></h4>
<p>&ensp; A/B 테스팅의 결과는 계절 변화나 취향 변화 등에 따라 달라질 수 있다. 작년 겨울에 <strong>A/B 테스팅을 하여 얻은 결론은 언제까지 유효할까?</strong></p>
<p>&ensp; A/B 테스트는 <strong>시공간적 보편성에 대한 가정</strong>을 깔고 있다. 이 가정은 비지니스 맥락에서는 매우 약해지기 때문에 <strong>확실성을 유지</strong>하기 위해서는 실험을 <strong>지속적이고 반복적</strong>으로 시행해야 한다. 하지만 앞서 말한 비용의 문제와 결부되기 때문에 많은 고민이 필요하다.</p>
<blockquote>
<h3 id="그럼에도-ab-테스트는-좋다"><strong><em>&quot;그럼에도 A/B 테스트는 좋다!&quot;</em></strong></h3>
</blockquote>
<p>&ensp; A/B 테스트는 오래된 역사를 가지고 있으며 지금도 여전히 데이터 분석 분야에서 유용한 실험 방법으로 사용되고 있다. 대부분의 IT기업에서는 이것을 사용하고 있으며 데이터를 기반으로 한 의사결정 과정에서 빠질 수 없다.</p>
<p>&ensp; 주의사항과 단점들을 고민하여 좋은 실험을 설계할 수 있는 것 또한 데이터를 보는 사람의 능력일 수 있겠다. 그것을 위해 끊임 없이 공부하고 고민하자.</p>
<hr>
<p>© 참고
<a href="https://brunch.co.kr/@digitalnative/19">https://brunch.co.kr/@digitalnative/19</a>
<a href="https://juun42.tistory.com/56">https://juun42.tistory.com/56</a>
<a href="https://www.elancer.co.kr/blog/view?seq=76">https://www.elancer.co.kr/blog/view?seq=76</a>
<a href="https://boxnwhis.kr/2015/01/29/a_b_testing.html">https://boxnwhis.kr/2015/01/29/a_b_testing.html</a>
<a href="https://bodi.tistory.com/">https://bodi.tistory.com/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[데이터분석]A/B Test 알아보기 - 2]]></title>
            <link>https://velog.io/@kyeonghun_kim/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B6%84%EC%84%9DAB-Test-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0-2</link>
            <guid>https://velog.io/@kyeonghun_kim/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B6%84%EC%84%9DAB-Test-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0-2</guid>
            <pubDate>Tue, 23 Jan 2024 09:24:45 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>&ensp;지난 시간에는 A/B Test가 무엇인지, 왜 하는지에 대해 알아보았다. 그렇다면 A/B Test는 <strong>어떻게</strong> 하는 것인지에 대해 알아보자.</p>
</blockquote>
<p>📌 <em>이전 글 <a href="https://velog.io/@kyeonghun_kim/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B6%84%EC%84%9DAB-Test-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0-1">[데이터분석]A/B Test 알아보기 -1</a></em></p>
<h3 id="✍🏻-ab-test-하는-법">✍🏻 A/B Test 하는 법</h3>
<p>&ensp; 모든 실험에서 중요한 것처럼 <strong>A/B 테스트</strong>에서 가장 중요한 것은 <strong>사용자를 잘 나누는 것</strong>이다. 사용자를 나눌 때는 먼저 <strong>임의적</strong>으로 나눠야 한다. 임의적으로 나누지 않을 경우 두 집단의 차이가 무엇 때문에 발생하는 것인지 알 수 없기 때문이다. 또한 각각의 집단이 <strong>모집단을 대표</strong>할 수 있어야 한다.</p>
<p>&ensp; 사용자를 <strong>노출 분산 방식</strong>, <strong>사용자 분산 방식</strong>, <strong>시간대 분산 방식</strong>이 있다. 각각의 방식에 대한 특징은 다음과 같다.
<img src="https://velog.velcdn.com/images/kyeonghun_kim/post/96b6c90b-4f77-48f0-b773-f20448b59102/image.png" alt=""></p>
<p>&ensp; <strong>노출 분산 방식</strong>의 경우에는 <strong>일정 비율(5:5 or 9:1)</strong>의 비율에 따라 A와 B를 노출 시키는 방식이다. 특정 heavy user가 매우 많은 Action을 보여주더라도 노출 빈도에 따라 양쪽에 모두 포함될 수 있기 때문에 <strong>통계적 유의성</strong>이 높다고 할 수 있다. 하지만, 동일 사용자가 A와 B를 모두 볼 수 있기 때문에 사용자에게 혼란을 줄 수 있고 결과값에 Bias를 만들 수 있다는 단점이 있다.</p>
<p>&ensp; <strong>사용자 분산 방식</strong>의 경우에는 사용자의 <strong>고유 ID값</strong>에 따라 사용자 그룹을 나누고 각 집단에 A와 B를 보여주는 방식이다. 사용자에게 혼란을 적게 준다는 장점이 있으나 heavy user가 <strong>결과값에 많은 영향</strong>을 줄 수 있다는 단점이 있다.</p>
<p>&ensp; <strong>시간대 분산 방식</strong>은 시스템 설계상 노출 분산 방식이나 사용자 분산 방식이 어려울 경우 선택할 수 있다. 초(second), 분(minute), 시간(hour) 등의 단위로 분할하여 테스트를 진행하는 방식이다.</p>
<h3 id="🛠️-ab-테스트-단계별-프로세스">🛠️ A/B 테스트 단계별 프로세스</h3>
<h4 id="1-기존-사이트-분석research">1. 기존 사이트 분석(Research)</h4>
<p>&ensp; A/B 테스트를 시작하기에 앞서 <strong>기존에 운영되고 있는 서비스를 분석</strong>할 필요가 있다. 사용자가 가장 많이 방문한 페이지, 가장 많이 시간을 보낸 페이지 등 <strong>정량적인 데이터</strong>와 세션 기록, 사용자 설문 조사를 통한 <strong>정성적인 데이터</strong>를 확보해야 한다.</p>
<h4 id="2-가설-설정-및-가설-테스트-진행">2. 가설 설정 및 가설 테스트 진행</h4>
<p>&ensp; 데이터를 수집하고 분석이 완료되었다면 <strong>목표 달성에 필요한 KPI</strong>를 특정하고 이 목표을 달성할 수 있는 <strong>가설을 설정</strong>한다. 가설을 설정한 뒤에는 이 가설에 대한 신뢰도, 목표에 미치는 영향 등에 대한 <strong>가설 테스트</strong>를 진행한다.</p>
<h4 id="3-테스트-실행하기">3. 테스트 실행하기</h4>
<p>&ensp; 위에서 언급한 것과 같이 상황과 목표에 따라 테스트의 기간, 방법을 설정하고 A/B 테스트를 진행한다.</p>
<h4 id="4-테스트-결과-분석-및-변경사항-도입하기">4. 테스트 결과 분석 및 변경사항 도입하기</h4>
<p>&ensp; 테스트가 완료 되었다면 앞서 정한 <strong>지표가 어떻게 변화</strong>되었는지, 얼마만큼의 <strong>신뢰도를 확보</strong>하였는지 등을 분석하여 변경사항을 도입할지, 새로운 A/B 테스트를 진행할지 결정한다.</p>
<hr>
<p>© 참고
<a href="https://brunch.co.kr/@digitalnative/19">https://brunch.co.kr/@digitalnative/19</a>
<a href="https://juun42.tistory.com/56">https://juun42.tistory.com/56</a>
<a href="https://www.elancer.co.kr/blog/view?seq=76">https://www.elancer.co.kr/blog/view?seq=76</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[데이터분석]A/B Test 알아보기 - 1]]></title>
            <link>https://velog.io/@kyeonghun_kim/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B6%84%EC%84%9DAB-Test-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0-1</link>
            <guid>https://velog.io/@kyeonghun_kim/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B6%84%EC%84%9DAB-Test-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0-1</guid>
            <pubDate>Tue, 23 Jan 2024 08:25:26 GMT</pubDate>
            <description><![CDATA[<h2 id="ab-테스트">A/B 테스트</h2>
<blockquote>
</blockquote>
<h3 id="a와-b-둘-중-뭐가-더-좋아"><em>&quot;A와 B 둘 중 뭐가 더 좋아?&quot;</em></h3>
<h3 id="❓ab-테스트란">❓A/B 테스트란?</h3>
<p>&ensp;분할 테스트 또는 버킷 테스트라고도 불리며 두 가지 콘텐츠를 비교하여 방문자/뷰어가 더 높은 관심을 보이는 버전을 확인하는 것을 말한다.</p>
<p>&ensp;웹사이트 방문자, 고객 등을 <strong>두 집단으로 나누고</strong> 한 집단에게는 기존의 사이트를 보여 주고 다른 집단에게는 새로운 사이트를 보여준다. 이후, 두 집단 중 어떤 집단이 더 높은 성과를 보이는지 측정하여, 새 사이트가 기존 사이트에 비해 좋은지를 <strong>정량적으로 평가</strong>하는 방식으로 진행된다.</p>
<p>&ensp;여기서 성과란 실험 설계자가 목표로 했던 바에 따라 달라질 수 있는데 보통은 <strong>회원 가입율</strong>, <strong>재방문율</strong>, <strong>구매전환율</strong> 등이 있다.</p>
<h3 id="✍🏻ab-테스트를-하는-이유">✍🏻A/B 테스트를 하는 이유</h3>
<p>&ensp;<strong>A/B 테스트</strong>를 하는 이유는 <strong>상관관계</strong>에서 <strong>인과관계</strong>를 찾아내기 위함이다. 이러한 원인을 찾아내면 이것에 개입해서 설정한 목표를 달성할 수 있기 때문이다.</p>
<p>&ensp;상관관계와 인과관계는 유사하지만 다르다. 예를 들어 물놀이 사고를 예방하기 위한 목표를 설정했다고 가정하자. 조사 결과 아이스크림 판매량과 물놀이 사고가 높은 양의 상관관계를 가지고 있다는 것을 알아냈다. 이 경우, 아이스크림 판매량이 물놀이 사고와 인과관계를 가졌다고는 볼 수 없을 것이다.</p>
<p><img src="https://velog.velcdn.com/images/kyeonghun_kim/post/66d76e7a-217a-473e-a389-edd2977fbb3c/image.png" alt=""></p>
<p>&ensp;A/B 테스트의 가장 유명한 사례는 <strong>오바마 대통령의 선거 운동</strong>에서 찾아볼 수 있다. 2008년, 2012년 대선에서 오바마 대통령의 선거 캠프는 빅데이터 분석을 적극적으로 활용했다. 오바마 선거 사이트의 웹 개발 이사였던 카일 러쉬는 500여 건의 <strong>A/B 테스트</strong>를 통해 <strong>기부율의 49% 올렸고 이메일 수집률을 161%나 올릴 수 있었다</strong>고 한다.</p>
<hr>
<p>© 참고
<a href="https://xkcd.com/552/">https://xkcd.com/552/</a>
<a href="https://datarian.io/blog/a-b-testing">https://datarian.io/blog/a-b-testing</a>
<a href="https://www.mk.co.kr/news/business/7834043">https://www.mk.co.kr/news/business/7834043</a>
<a href="https://ko.wikipedia.org/wiki/A/B_%ED%85%8C%EC%8A%A4%ED%8A%B8">https://ko.wikipedia.org/wiki/A/B_%ED%85%8C%EC%8A%A4%ED%8A%B8</a></p>
]]></description>
        </item>
    </channel>
</rss>