diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 000000000000..5ae69bd5f0e8 --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +3.2.5 diff --git a/Gemfile b/Gemfile index 3be9c3cd812e..f266a85658e3 100644 --- a/Gemfile +++ b/Gemfile @@ -1,2 +1,10 @@ source "https://rubygems.org" -gemspec +gemspecs + +gem "minimal-mistakes-jekyll" + +gem "webrick", "~> 1.7" + +# Windows does not include zoneinfo files, so bundle the tzinfo-data gem +gem 'tzinfo' +gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby] diff --git a/_config.yml b/_config.yml index af7d57221b02..d0d5c431b48d 100644 --- a/_config.yml +++ b/_config.yml @@ -15,28 +15,28 @@ minimal_mistakes_skin : "default" # "air", "aqua", "contrast", "dark", "dirt", "neon", "mint", "plum", "sunrise" # Site Settings -locale : "en-US" rtl : # true, false (default) # turns direction of the page into right to left for RTL languages -title : "Site Title" -title_separator : "-" -subtitle : # site tagline that appears below site title in masthead -name : "Your Name" -description : "An amazing website." -url : # the base hostname & protocol for your site e.g. "https://mmistakes.github.io" +locale : "ko-KR" +title : "YoonkieNote" +title_separator : "|" +subtitle : "Simply, Deeply" +name : "YoonkieNote" +description : "나만의 개발 공부 일지" +url : "https://minyoongi96.github.io" baseurl : # the subpath of your site, e.g. "/blog" repository : # GitHub username/repo-name e.g. "mmistakes/minimal-mistakes" teaser : # path of fallback teaser image, e.g. "/assets/images/500x300.png" logo : # path of logo image to display in the masthead, e.g. "/assets/images/88x88.png" masthead_title : # overrides the website title displayed in the masthead, use " " for no title -breadcrumbs : # true, false (default) +breadcrumbs : true # true, false (default) words_per_minute : 200 enable_copy_code_button : # true, false (default) copyright : # "copyright" name, defaults to site.title copyright_url : # "copyright" URL, defaults to site.url comments: - provider : # false (default), "disqus", "discourse", "facebook", "staticman", "staticman_v2", "utterances", "giscus", "custom" + provider : "disqus" # (default), "disqus", "discourse", "facebook", "staticman", "staticman_v2", "utterances", "giscus", "custom" disqus: - shortname : # https://help.disqus.com/customer/portal/articles/466208-what-s-a-shortname- + shortname : "yoonkienote" discourse: server : # https://meta.discourse.org/t/embedding-discourse-comments-via-javascript/31963 , e.g.: meta.discourse.org facebook: @@ -84,7 +84,7 @@ google: # SEO Related google_site_verification : bing_site_verification : -naver_site_verification : +naver_site_verification : "efd9a2ff3152882228024e338d6cffdcac9804f6" yandex_site_verification : baidu_site_verification : @@ -105,60 +105,40 @@ social: # Analytics analytics: - provider : # false (default), "google", "google-universal", "google-gtag", "custom" + provider : "google-gtag" # false (default), "google", "google-universal", "google-gtag", "custom" google: - tracking_id : - anonymize_ip : # true, false (default) + tracking_id : "G-D9JJMVGYQH" + anonymize_ip : false # Site Author author: - name : "Your Name" - avatar : # path of avatar image, e.g. "/assets/images/bio-photo.jpg" - bio : "I am an **amazing** person." - location : "Somewhere" + name : "minyoongi96" + avatar : "/assets/images/MyProfile.png" # path of avatar image, e.g. "/assets/images/bio-photo.jpg" + bio : "웹 개발 끄적끄적" + location : #"Somewhere" email : links: - label: "Email" icon: "fas fa-fw fa-envelope-square" - # url: "mailto:your.name@email.com" - - label: "Website" - icon: "fas fa-fw fa-link" - # url: "https://your-website.com" - - label: "Twitter" - icon: "fab fa-fw fa-twitter-square" - # url: "https://twitter.com/" - - label: "Facebook" - icon: "fab fa-fw fa-facebook-square" - # url: "https://facebook.com/" + url: "zoobon63@gmail.com" - label: "GitHub" icon: "fab fa-fw fa-github" - # url: "https://github.com/" + url: "https://github.com/minyoongi96" - label: "Instagram" icon: "fab fa-fw fa-instagram" - # url: "https://instagram.com/" + url: "https://instagram.com/yoon___kie" # Site Footer footer: links: - - label: "Twitter" - icon: "fab fa-fw fa-twitter-square" - # url: - - label: "Facebook" - icon: "fab fa-fw fa-facebook-square" - # url: + - label: "GitHub" icon: "fab fa-fw fa-github" - # url: - - label: "GitLab" - icon: "fab fa-fw fa-gitlab" - # url: - - label: "Bitbucket" - icon: "fab fa-fw fa-bitbucket" - # url: + url: "https://github.com/minyoongi96" - label: "Instagram" icon: "fab fa-fw fa-instagram" - # url: + url: "https://instagram.com/yoon___kie" # Reading Files @@ -227,33 +207,9 @@ sass: # Outputting permalink: /:categories/:title/ -timezone: # https://en.wikipedia.org/wiki/List_of_tz_database_time_zones - - -# Pagination with jekyll-paginate -paginate: 5 # amount of posts to show +paginate: 10 # amount of posts to show paginate_path: /page:num/ - -# Pagination with jekyll-paginate-v2 -# See https://github.com/sverrirs/jekyll-paginate-v2/blob/master/README-GENERATOR.md#site-configuration -# for configuration details -pagination: - # Set enabled to true to use paginate v2 - # enabled: true - debug: false - collection: 'posts' - per_page: 10 - permalink: '/page/:num/' - title: ':title - page :num' - limit: 0 - sort_field: 'date' - sort_reverse: true - category: 'posts' - tag: '' - locale: '' - trail: - before: 2 - after: 2 +timezone: Asia/Seoul # Plugins (previously gems:) @@ -290,16 +246,16 @@ tag_archive: type: liquid path: /tags/ # https://github.com/jekyll/jekyll-archives -# jekyll-archives: -# enabled: -# - categories -# - tags -# layouts: -# category: archive-taxonomy -# tag: archive-taxonomy -# permalinks: -# category: /categories/:name/ -# tag: /tags/:name/ +jekyll-archives: + enabled: + - categories + - tags + layouts: + category: archive-taxonomy + tag: archive-taxonomy + permalinks: + category: /categories/:name/ + tag: /tags/:name/ # HTML Compression @@ -320,6 +276,7 @@ defaults: layout: single author_profile: true read_time: true - comments: # true + comments: true share: true related: true + show_date : true diff --git a/_data/navigation.yml b/_data/navigation.yml index 6f30866f3bed..91303554abb0 100644 --- a/_data/navigation.yml +++ b/_data/navigation.yml @@ -1,7 +1,9 @@ # main links main: - - title: "Quick-Start Guide" - url: https://mmistakes.github.io/minimal-mistakes/docs/quick-start-guide/ + - title: "Category" + url: /categories/ + - title: "Tag" + url: /tags/ # - title: "About" # url: https://mmistakes.github.io/minimal-mistakes/about/ # - title: "Sample Posts" @@ -9,4 +11,30 @@ main: # - title: "Sample Collections" # url: /collection-archive/ # - title: "Sitemap" - # url: /sitemap/ \ No newline at end of file + # url: /sitemap/ + +docs: + - title: Back-end + childen: + - title: "Spring" + url: spring/ + category: "springboot" + - title: AI + childen: + - title: "Deep Learning" + url: deeplearning/ + category: "딥러닝" + - title: "Machine Learning" + url: machinelearning/ + category: "머신러닝" + - title: 알고리즘 + childen: + - title: "Algorithm" + url: algorithm/ + category: "자료구조-알고리즘" + - title: Git + childen: + - title: "Git" + url: git/ + category: "git" + \ No newline at end of file diff --git a/_includes/breadcrumbs.html b/_includes/breadcrumbs.html index 929725ef830c..c38e050adcf5 100644 --- a/_includes/breadcrumbs.html +++ b/_includes/breadcrumbs.html @@ -11,7 +11,7 @@ {% else %} {% assign crumb_path = site.category_archive.path %} {% endif %} - +<!-- <nav class="breadcrumbs"> <ol itemscope itemtype="https://schema.org/BreadcrumbList"> {% assign crumbs = page.url | split: '/' %} @@ -38,3 +38,4 @@ {% endfor %} </ol> </nav> +--> \ No newline at end of file diff --git a/_includes/nav_list b/_includes/nav_list index d25f9e9480bc..e63305e1e737 100644 --- a/_includes/nav_list +++ b/_includes/nav_list @@ -12,7 +12,7 @@ {% else %} <span class="nav__sub-title">{{ nav.title }}</span> {% endif %} - +<<<<<<< HEAD {% if nav.children != null %} <ul> {% for child in nav.children %} @@ -22,6 +22,18 @@ {% endif %} </li> {% endfor %} +======= + + {% if nav.children != null %} + <ul> + {% for child in nav.children %} + {% assign category = site.categories[child.category] | where_exp: "item", "item.hidden != true" %} + <li><a href="{{ child.url | relative_url }}"{% if child.url == page.url %} class="active"{% endif %}>{{ child.title }} ({{ category.size }})</a></li> + {% endfor %} + </ul> + {% endif %} + </li> +>>>>>>> master {% endfor %} </ul> </nav> diff --git a/_includes/nav_list_bak b/_includes/nav_list_bak new file mode 100644 index 000000000000..a035a5bd7b15 --- /dev/null +++ b/_includes/nav_list_bak @@ -0,0 +1,26 @@ +{% assign navigation = site.data.navigation[include.nav] %} + +<nav class="nav__list"> + {% if page.sidebar.title %}<h3 class="nav__title" style="padding-left: 0;">{{ page.sidebar.title }}</h3>{% endif %} + <input id="ac-toc" name="accordion-toc" type="checkbox" /> + <label for="ac-toc">{{ site.data.ui-text[site.locale].menu_label | default: "Toggle Menu" }}</label> + <ul class="nav__items"> + {% for nav in navigation %} + <li> + {% if nav.url %} + <a href="{{ nav.url | relative_url }}"><span class="nav__sub-title">{{ nav.title }}</span></a> + {% else %} + <span class="nav__sub-title">{{ nav.title }}</span> + {% endif %} + + {% if nav.children != null %} + <ul> + {% for child in nav.children %} + <li><a href="{{ child.url | relative_url }}"{% if child.url == page.url %} class="active"{% endif %}>{{ child.title }}</a></li> + {% endfor %} + </ul> + {% endif %} + </li> + {% endfor %} + </ul> +</nav> diff --git a/_includes/page__meta.html b/_includes/page__meta.html index 3d228c921205..575542ef23eb 100644 --- a/_includes/page__meta.html +++ b/_includes/page__meta.html @@ -9,14 +9,14 @@ <time datetime="{{ date | date_to_xmlschema }}">{{ date | date: date_format }}</time> </span> {% endif %} - - {% if document.read_time and document.show_date %}<span class="page__meta-sep"></span>{% endif %} +<!--주석: 소요시간 제거--> + {% if document.read_time and document.show_date %}<!--<span class="page__meta-sep"></span>-->{% endif %} {% if document.read_time %} {% assign words_per_minute = document.words_per_minute | default: site.words_per_minute | default: 200 %} {% assign words = document.content | strip_html | number_of_words %} - <span class="page__meta-readtime"> + <!--<span class="page__meta-readtime"> <i class="far {% if include.type == 'grid' and document.read_time and document.show_date %}fa-fw {% endif %}fa-clock" aria-hidden="true"></i> {% if words < words_per_minute %} {{ site.data.ui-text[site.locale].less_than | default: "less than" }} 1 {{ site.data.ui-text[site.locale].minute_read | default: "minute read" }} @@ -25,7 +25,7 @@ {% else %} {{ words | divided_by: words_per_minute }} {{ site.data.ui-text[site.locale].minute_read | default: "minute read" }} {% endif %} - </span> + </span>--> {% endif %} </p> {% endif %} diff --git a/_includes/sidebar.html b/_includes/sidebar.html index a4ca1ca78151..4c25cc498ab8 100644 --- a/_includes/sidebar.html +++ b/_includes/sidebar.html @@ -5,7 +5,7 @@ {% for s in page.sidebar %} {% if s.image %} <img src="{{ s.image | relative_url }}" - alt="{% if s.image_alt %}{{ s.image_alt }}{% endif %}"> + alt="{% if s.image_alt %}{{ s.image_alt }}{% endif %}"> {% endif %} {% if s.title %}<h3>{{ s.title }}</h3>{% endif %} {% if s.text %}{{ s.text | markdownify }}{% endif %} diff --git a/_pages/category-archive.md b/_pages/category-archive.md new file mode 100644 index 000000000000..3cd6f6e06761 --- /dev/null +++ b/_pages/category-archive.md @@ -0,0 +1,7 @@ +--- +title: "Category" +layout: categories +permalink: /categories/ +author_profile: true +sidebar_main: true +--- \ No newline at end of file diff --git a/_pages/tag-archive.md b/_pages/tag-archive.md new file mode 100644 index 000000000000..58ae8e76afc6 --- /dev/null +++ b/_pages/tag-archive.md @@ -0,0 +1,7 @@ +--- +title: "Tag" +layout: tags +permalink: /tags/ +author_profile: true +sidebar_main: true +--- \ No newline at end of file diff --git a/_posts/2022-01-05-Numpy.md b/_posts/2022-01-05-Numpy.md new file mode 100644 index 000000000000..b2cda2142eb9 --- /dev/null +++ b/_posts/2022-01-05-Numpy.md @@ -0,0 +1,687 @@ +--- +layout: single +title: "[Python] Numpy 개인 정리" +categories: Python +tag: [python, Numpy, 파이썬] +toc: true +toc_sticky: true +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 8px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + + .output_prompt { + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; + -webkit-overflow-scrolling: touch; + padding: 0.8rem; + margin-top: 0; + margin-bottom: 15px; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: $code-text-color; + border: solid 1px $border-color; + border-radius: 0.3rem; + word-break: normal; + white-space: pre; + } + + .dataframe tbody tr th:only-of-type { + vertical-align: middle; + } + + .dataframe tbody tr th { + vertical-align: top; + } + + .dataframe thead th { + text-align: center !important; + padding: 8px; + } + + .page__content p { + margin: 0 0 0px !important; + } + + .page__content p > strong { + font-size: 0.8rem !important; + } + + </style> +</head> + + +```python +import numpy as np +``` + + +```python +# numpy의 배열 생성 +list1 = [1,2,3,4,5] +list2 = [[1,2,3,4],[5,6,7,8],[9,10,11,12]] + +arr1 = np.array(list1) # 1차원 배열 +arr1 +``` + + + + + array([1, 2, 3, 4, 5]) + + + + +```python +arr2 = np.array(list2) # 2차원 배열 +arr2 +``` + + + + + array([[ 1, 2, 3, 4], + [ 5, 6, 7, 8], + [ 9, 10, 11, 12]]) + + + + +```python +# 배열의 크기 확인 +print(arr1.shape) +print(arr2.shape) +``` + + (5,) + (3, 4) + + + +```python +# size는 배열 내의 요소 개수 +print(arr1.size) +print(arr2.size) +``` + + 5 + 12 + + + +```python +# 배열의 데이터 타입 확인 +print(arr1.dtype) +print(arr2.dtype) +``` + + int64 + int64 + + + +```python +# 배열의 차원 확인 +print(arr1.ndim) +print(arr2.ndim) +``` + + 1 + 2 + + + +```python +# 배열 초기화 +arr_zero = np.zeros((3,3)) +print(arr_zero) +print(arr_zero.dtype) + +arr_one = np.ones((2,3)) +print(arr_one) +``` + + [[0. 0. 0.] + [0. 0. 0.] + [0. 0. 0.]] + float64 + [[1. 1. 1.] + [1. 1. 1.]] + + + +```python +# 특정 값으로 배열 초기화 +# np.full(차원, 데이터) + +arr_full = np.full((4,3), 'z') +print(arr_full) +``` + + [['z' 'z' 'z'] + ['z' 'z' 'z'] + ['z' 'z' 'z'] + ['z' 'z' 'z']] + + + +```python +# 범위를 지정해서 배열 생성 +# arange(시작점, 끝점+1, 데이터 폭) +arr = np.arange(1,51,5) +print(arr) +print(arr.shape) +print(arr.size) +``` + + [ 1 6 11 16 21 26 31 36 41 46] + (10,) + 10 + + + +```python +# 배열 생성 시 타입 지정 +arr_type = np.array([1,2,2,5,3,4], dtype=np.str0) + +print(arr_type) + +# 타입 변경 +arr_type = arr_type.astype('int64') +print(arr_type) +``` + + ['1' '2' '2' '5' '3' '4'] + [1 2 2 5 3 4] + + + +```python +# 랜덤 값으로 배열 생성 +# np.random.rand(차원 수) : 랜덤 실수값 +arr_random = np.random.rand(2,3) +print(arr_random) +``` + + [[0.069824 0.56538797 0.08239869] + [0.05746204 0.47192185 0.65286904]] + + + +```python +# np.random.randint(시작점, 끝점, size=(차원 수)) : 시작점부터 끝점-1 까지의 랜덤 정수 값 +arr_random2 = np.random.randint(1,11, size=(3,3)) +arr_random2 +``` + + + + + array([[ 3, 10, 5], + [ 1, 3, 8], + [ 9, 9, 8]]) + + + + +```python +# array 연산 +array1 = np.array([1,2,3]) +array2 = np.array([4,5,6]) + +print(array1+array2) +print(array1 / array2) +print(array1 // array2) +print(array1 % array2) +``` + + [5 7 9] + [0.25 0.4 0.5 ] + [0 0 0] + [1 2 3] + + +- BMI 지수 구하기 + + +```python +# txt파일 불러오기 +data = np.loadtxt('/Users/min-yungi/Desktop/20210905pr/data/height_weight.txt', delimiter = ',') +print(data) +print(data.shape) +print(data[1,0]) +``` + + [[175.2 180.3 175. 169.2 185.2 188. 177.6 178.2 177. 179. ] + [ 65.6 88. 79.2 69.3 55. 71.2 73. 68.9 74. 82. ]] + (2, 10) + 65.6 + + + +```python +height = data[0] * 0.01 +print(height) +``` + + [1.752 1.803 1.75 1.692 1.852 1.88 1.776 1.782 1.77 1.79 ] + + + +```python +weight = data[1] +print(weight) +``` + + [65.6 88. 79.2 69.3 55. 71.2 73. 68.9 74. 82. ] + + + +```python +bmi = weight/(height**2) +print(bmi) +``` + + [21.37153104 27.07018468 25.86122449 24.20652885 16.03543423 20.14486193 + 23.14392095 21.69720651 23.62028791 25.59220998] + + + - Indexing & Slicing + + +```python +array7 = np.arange(10) +print(array7) + +print(array7[3:8]) + +array7[3:8] = 10 +print(array7) +``` + + [0 1 2 3 4 5 6 7 8 9] + [3 4 5 6 7] + [ 0 1 2 10 10 10 10 10 8 9] + + + +```python +# 2차원 배열 생성 +array8 = np.arange(50).reshape(5,10) +print(array8) +print(array8.shape) + +# 2차원 배열 슬라이싱 +print(array8[1:4,4:9]) +``` + + [[ 0 1 2 3 4 5 6 7 8 9] + [10 11 12 13 14 15 16 17 18 19] + [20 21 22 23 24 25 26 27 28 29] + [30 31 32 33 34 35 36 37 38 39] + [40 41 42 43 44 45 46 47 48 49]] + (5, 10) + [[14 15 16 17 18] + [24 25 26 27 28] + [34 35 36 37 38]] + + +- Boolean 색인 + + +```python +name = np.array(['윤기','석준','원빈','은수','태헌']) +name +``` + + + + + array(['윤기', '석준', '원빈', '은수', '태헌'], dtype='<U2') + + + + +```python +arr_bool = np.array([True,False,False,True,True]) +arr_bool +``` + + + + + array([ True, False, False, True, True]) + + + + +```python +# boolean데이터의 array이용한 인덱싱 +name[arr_bool] +``` + + + + + array(['윤기', '은수', '태헌'], dtype='<U2') + + + + +```python +name_score = np.array([[60,60],[70,70],[80,80],[90,90],[50,50]]) +name_score +``` + + + + + array([[60, 60], + [70, 70], + [80, 80], + [90, 90], + [50, 50]]) + + + + +```python +name_score[name=='석준'] +``` + + + + + array([[70, 70]]) + + + + +```python +name_score[arr_bool] +``` + + + + + array([[60, 60], + [90, 90], + [50, 50]]) + + + +- sum함수 + + +```python +arr = np.random.randint(1,10, size=(2,2)) +arr +``` + + + + + array([[4, 1], + [6, 6]]) + + + + +```python +print(arr.sum()) +print(np.sum(arr)) +``` + + 17 + 17 + + +- mean함수 + + +```python +arr = np.random.randint(1,10, size=(2,2)) +arr +``` + + + + + array([[7, 6], + [3, 1]]) + + + + +```python +print(arr.mean()) +print(np.mean(arr)) +``` + + 4.25 + 4.25 + + +- 영화 평점 분석 + + +```python +data = np.loadtxt('/Users/min-yungi/Desktop/20210905pr/data/ratings.dat', delimiter='::', + dtype='int64') +data +``` + + + + + array([[ 1, 1193, 5, 978300760], + [ 1, 661, 3, 978302109], + [ 1, 914, 3, 978301968], + ..., + [ 6040, 562, 5, 956704746], + [ 6040, 1096, 4, 956715648], + [ 6040, 1097, 4, 956715569]]) + + + + +```python +print(data.ndim) +print(data.shape) +``` + + 2 + (1000209, 4) + + + +```python +# 모든 평점의 평균 구하기 +rating_all_mean = data[:,2].mean() +rating_all_mean +``` + + + + + 3.581564453029317 + + + + +```python +# 각 사용자별 평점 평균 구하기 +userID = np.unique(data[:,0]) +print(userID) +print(userID.shape) +``` + + [ 1 2 3 ... 6038 6039 6040] + (6040,) + + + +```python +# 사용자 id가 1인 사람의 데이터 +print(data[data[:,0]==1].shape) +``` + + (53, 4) + + + +```python +# 사용자 id가 1인 사람의 평점 데이터 +print(data[data[:,0]==1,2]) +``` + + [5 3 3 4 5 3 5 5 4 4 5 4 4 4 5 4 3 4 5 4 3 3 5 5 3 5 4 4 4 3 4 4 4 4 4 4 5 + 5 4 5 5 5 4 4 4 5 5 4 5 4 4 4 4] + + + +```python +# 사용자 id가 1인 사람의 평균 평점 +print(data[data[:,0]==1,2].mean()) +``` + + 4.188679245283019 + + + +```python +user_means = [] +for ID in userID: + user_mean = data[data[:,0]==ID,2].mean() + user_means.append([ID,user_mean]) +user_means[:5] +``` + + + + + [[1, 4.188679245283019], + [2, 3.7131782945736433], + [3, 3.9019607843137254], + [4, 4.190476190476191], + [5, 3.1464646464646466]] + + + + +```python +# csv파일로 저장 +np.savetxt('/Users/min-yungi/Desktop/20210905pr/data/movie_id_mean.csv', user_means, + delimiter=',' ,fmt='%3f') +``` + + +```python +# 사용자 id가 2인 사람의 평균 평점 +user_means[1] +``` + + + + + [2, 3.7131782945736433] + + + + +```python +data +``` + + + + + array([[ 1, 1193, 5, 978300760], + [ 1, 661, 3, 978302109], + [ 1, 914, 3, 978301968], + ..., + [ 6040, 562, 5, 956704746], + [ 6040, 1096, 4, 956715648], + [ 6040, 1097, 4, 956715569]]) + + + + +```python +# 평균 평점이 4.5이상인 사용자id 구하기 +arr_means = np.array(user_means) +over4 = arr_means[arr_means[:,1]>=4.5,0].astype('int') +over4 +``` + + + + + array([ 91, 101, 215, 233, 266, 283, 288, 307, 372, 446, 447, + 451, 567, 661, 682, 761, 764, 951, 953, 989, 1021, 1131, + 1315, 1349, 1378, 1437, 1569, 1670, 1702, 1856, 1863, 1940, 1959, + 1986, 2118, 2155, 2243, 2339, 2363, 2497, 2510, 2574, 2593, 2694, + 2726, 2791, 2838, 2849, 2922, 2964, 3036, 3073, 3210, 3282, 3287, + 3306, 3324, 3437, 3461, 3596, 3604, 3699, 3738, 3864, 3902, 4136, + 4176, 4187, 4189, 4273, 4518, 4584, 4589, 4595, 4634, 4649, 4755, + 4801, 4904, 4925, 4982, 5003, 5069, 5073, 5102, 5165, 5212, 5255, + 5343, 5427, 5499, 5542, 5582, 5609, 5696, 5768, 5839, 5862, 5936, + 5984]) + + + + +```python +# 최고 평균 평점 구하기 +max_mean = arr_means[:,1].max() +max_mean +``` + + + + + 4.962962962962963 + + + + +```python +# 최고 평점 구하기 +data[:,2].max() +``` + + + + + 5 + + diff --git a/_posts/2022-01-07-Queue.md b/_posts/2022-01-07-Queue.md new file mode 100644 index 000000000000..345a273c3e48 --- /dev/null +++ b/_posts/2022-01-07-Queue.md @@ -0,0 +1,383 @@ +--- +layout: single +title: "[자료구조/알고리즘] 큐(Queue)와 덱(Deque)" +categories: 자료구조/알고리즘 +tag: [python, 파이썬, Queue, Deque, 큐, 덱, SinglyLinkedList, 자료구조, 알고리즘] +toc: true +toc_sticky: true +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 8px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + + .output_prompt { + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; + -webkit-overflow-scrolling: touch; + padding: 0.8rem; + margin-top: 0; + margin-bottom: 15px; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: $code-text-color; + border: solid 1px $border-color; + border-radius: 0.3rem; + word-break: normal; + white-space: pre; + } + + .dataframe tbody tr th:only-of-type { + vertical-align: middle; + } + + .dataframe tbody tr th { + vertical-align: top; + } + + .dataframe thead th { + text-align: center !important; + padding: 8px; + } + + .page__content p { + margin: 0 0 0px !important; + } + + .page__content p > strong { + font-size: 0.8rem !important; + } + + </style> +</head> + +### 큐(Queue) + +<br/> + ++ FIFO(선입선출) 형식의 자료구조 ++ 먼저 들어온 데이터가 순서대로 먼저 나가는 구조입니다. +스택과 다르게 한쪽에서 데이터가 추가되고, 다른 한쪽에서 데이터가 삭제되는 구조입니다. + +#### 리스트를 이용한 큐 구현 + +<br/> + ++ enqueue : 큐의 맨 뒤쪽에 항목 삽입 ++ dequeue : 큐 맨 앞쪽의 항목을 반환하고 제거 ++ peek/front : 큐의 맨 앞쪽 항목을 조회 ++ empty : 큐가 비었는지 확인 ++ size : 큐의 크기를 확인 + + +```python +# 리스트를 이용한 Queue 구현 +class Queue(object): + def __init__(self): + self.items = [] # 값을 담아줄 큐 배열 + + # 큐가 비었는지 확인하는 함수(Empty : True / Not Empty : false) + def isEmpty(self): + return not self.items + + def dequeue(self): + # 큐가 비어있다면 + if self.isEmpty(): + print("Queue is Empty!") + + # 그렇지 않으면 + else: + value = self.items.pop(0) # 큐 배열의 0번째 값을 반환 + return value + + + def enqueue(self, item): + self.items.append(item) # 큐 배열의 맨 뒤에 값을 삽입 + + def size(self): + return len(self.items) # 큐 배열의 크기를 반환 + + def peek(self): + if self.isEmpty(): + print("Queue is Empty!") + + else: + return self.items[0] + + def __repr__(self): + return repr(self.items) +``` + +```python +if __name__ == "__main__": + queue = Queue() + print("큐가 비었습니까? {}".format(queue.isEmpty())) + print("큐에 값을 추가합니다.") + for i in range(10): + queue.enqueue(i) + print("Queue : {}".format(queue)) + print("큐의 크기 : {}".format(queue.size())) + print("peek : {}".format(queue.peek())) + print("dequeue : {}".format(queue.dequeue())) + print("peek : {}".format(queue.peek())) + print("Queue : {}".format(queue)) +``` + + 큐가 비었습니까? True + 큐에 값을 추가합니다. + Queue : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + 큐의 크기 : 10 + peek : 0 + dequeue : 0 + peek : 1 + Queue : [1, 2, 3, 4, 5, 6, 7, 8, 9] + + +#### 노드를 이용한 큐(Queue) 구현 ++ 노드를 이용해서 SingleLinkedList로 구현합니다. + + +```python +# Node를 이용해 큐 구현 + +class Node(object): + def __init__(self, value = None, pointer = None): + self.value = value + self.pointer = pointer + +class LinkedQueue(object): + def __init__(self): + self.head = None # 큐의 맨 앞 + self.tail = None # 큐의 맨 뒤 + self.count = 0 + + def isEmpty(self): + return not bool(self.head) + + def dequeue(self): + if self.isEmpty(): + print("Queue is Empty!") + + else: + node = self.head + self.head = self.head.pointer + self.count -= 1 + + if self.head is None: # 데이터를 삭제함으로써 큐가 빌 경우 + # head와 tail은 같은 노드를 바라보고 있었으므로 + self.tail = None # tail값도 비워주기 + + return node.value + + def enqueue(self, value): + node = Node(value) + if not self.head: # head가 비어있다면 + self.head = node + self.tail = node + else: + self.tail.pointer = node # 현재 tail의 포인터를 node와 연결 + self.tail = node # 연결된 node를 tail로 지정 + self.count += 1 + + def size(self): + return self.count + + def peek(self): + if self.head: + return self.head.value + else: + print("Queue is Empty!") + + def printQueue(self): + node = self.head + while node: + print(node.value, end = " ") + node = node.pointer + print() +``` + +```python +if __name__ == "__main__": + queue = LinkedQueue() + print("스택이 비었습니까? {0}".format(queue.isEmpty())) + print("스택에 값을 추가합니다.") + + for i in range(10): + queue.enqueue(i) + queue.printQueue() + print("큐의 크기는? {0}".format(queue.size())) + print("peek : {}".format(queue.peek())) + print("pop : {}".format(queue.dequeue())) + print("peek : {}".format(queue.peek())) + queue.printQueue() + +# for i in range(9): +# queue.dequeue() +# print(queue.tail) + +``` + + 스택이 비었습니까? True + 스택에 값을 추가합니다. + 0 1 2 3 4 5 6 7 8 9 + 큐의 크기는? 10 + peek : 0 + pop : 0 + peek : 1 + 1 2 3 4 5 6 7 8 9 + + +LinkedQueue의 head는 큐의 맨 앞, tail은 맨 뒤를 의미합니다. + +<br/> + +Enqueue(데이터 추가)할 때는 데이터 값을 노드 형식(value, pointer)으로 변수 node에 대입한 후 +head가 비어있다면 > 큐에서 유일한 값이 되므로 head와 tail이 모두 추가된 node를 가리키게 됩니다. + +<br/> + +head가 비어있지 않을땐 > 들어오는 값을 큐에 우선 연결해준 후, tail의 위치를 마지막으로 변경해주면 됩니다. +tail의 다음값으로 지정하기 위해 우선 기존의 tail.pointer에 변수 node를 넣어주고, 추가된 노드를 tail로 지정해줍니다. + +<br/> + +Dequeue(데이터 삭제)할 때는 큐의 맨 앞, head를 반환해주기 위해 임시 node변수에 대입합니다. +그 후, head에 head.pointer(즉, 기존 head에서 가리키는 다음 노드)를 넣어줍니다. + +<br/> + +마지막으로 임시 node에 저장된 기존 head값을 return해주기 전에\! +큐의 유일한 노드가 삭제 될 경우는 head와 tail이 그 노드를 동시에 바라보고 있었기 때문에 +tail도 None으로 초기화 해주어야 합니다. + +### 덱(Deque) + +<br/> + ++ 덱(Double Ended Queue)은 큐와 다르게 양쪽 끝에서 항목을 조회, 삽입, 삭제가 가능합니다. +(스택과 큐의 융합) ++ 위에서 구현했던 큐 리스트를 그대로 상속해서 덱을 구현해보겠습니다. + + +```python +# 리스트를 활용한 덱 구현 +class Deque(Queue): # 큐 리스트를 상속 + # enqueue 반대로 하기 + def enqueue_front(self, item): + self.items.insert(0, item) # 리스트 0번째에 값 추가 + + # dequeue 반대로 하기 + def dequeue_back(self): + if self.isEmpty(): + print("Queue is Empty!") + else: + value = self.items.pop() # 리스트 가장 뒤에 있는 값을 삭제, 반환 + return value +``` + +```python +if __name__ == "__main__": + deque = Deque() + print("덱이 비었습니까? {}".format(deque.isEmpty())) + print("덱에 값을 추가합니다.") + for i in range(1,20,2): + deque.enqueue(i) + print("덱 : {}".format(deque)) + print("peek : {0}".format(deque.peek())) + print("dequeue : {0}".format(deque.dequeue())) + print("덱 : {0}".format(deque)) + deque.enqueue_front(50) + print("덱 : {0}".format(deque)) + print("dequeue_back : {}".format(deque.dequeue_back())) + print("덱 : {0}".format(deque)) +``` + + 덱이 비었습니까? True + 덱에 값을 추가합니다. + 덱 : [1, 3, 5, 7, 9, 11, 13, 15, 17, 19] + peek : 1 + dequeue : 1 + 덱 : [3, 5, 7, 9, 11, 13, 15, 17, 19] + 덱 : [50, 3, 5, 7, 9, 11, 13, 15, 17, 19] + dequeue_back : 19 + 덱 : [50, 3, 5, 7, 9, 11, 13, 15, 17] + + +#### 덱 모듈 사용하기 ++ Python의 Collections 모듈 안에 Deque라는 클래스가 이미 구현되어 있습니다. ++ Deque 클래스는 이중 연결 리스트(Double Linked List)로 구성되어서 여러 기능을 효율적으로 사용이 가능합니다. + + +```python +# 덱 모듈 사용하기 + +from collections import deque + +# deque() : 덱 생성 +q = deque(["Apple", "Banana", "Peach"]) +print(q) + +# 오른쪽에 값을 추가 +q.append("Strawberry") +print(q) + +# 왼쪽에 값을 추가 +q.appendleft("Mango") +print(q) + +# 왼쪽의 값을 반환 +q.popleft() +print(q) + +# 오른쪽의 값을 반환 +q.pop() +print(q) + +# rotate(n) : n의 길이만큼 양수면 오른쪽, 음수면 왼쪽으로 값들이 이동 +q = deque(['Mango', 'Apple', 'Banana', 'Peach', 'Strawberry']) +q.rotate(2) +print(q) + +q.rotate(-1) +print(q) + +# collections안의 Deque은 이중 연결 리스트 형식으로 되어져있기 때문에 +# 이러한 기능이 가능합니다. +``` + + deque(['Apple', 'Banana', 'Peach']) + deque(['Apple', 'Banana', 'Peach', 'Strawberry']) + deque(['Mango', 'Apple', 'Banana', 'Peach', 'Strawberry']) + deque(['Apple', 'Banana', 'Peach', 'Strawberry']) + deque(['Apple', 'Banana', 'Peach']) + deque(['Peach', 'Strawberry', 'Mango', 'Apple', 'Banana']) + deque(['Strawberry', 'Mango', 'Apple', 'Banana', 'Peach']) + + diff --git a/_posts/2022-01-07-Stack.md b/_posts/2022-01-07-Stack.md new file mode 100644 index 000000000000..841c9d5d731f --- /dev/null +++ b/_posts/2022-01-07-Stack.md @@ -0,0 +1,256 @@ +--- +layout: single +title: "[자료구조/알고리즘] 스택(Stack)" +categories: 자료구조/알고리즘 +tag: [python, 파이썬, Stack, 스택, 자료구조, 알고리즘] +toc: true +toc_sticky: true +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 8px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + + .output_prompt { + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; + -webkit-overflow-scrolling: touch; + padding: 0.8rem; + margin-top: 0; + margin-bottom: 15px; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: $code-text-color; + border: solid 1px $border-color; + border-radius: 0.3rem; + word-break: normal; + white-space: pre; + } + + .dataframe tbody tr th:only-of-type { + vertical-align: middle; + } + + .dataframe tbody tr th { + vertical-align: top; + } + + .dataframe thead th { + text-align: center !important; + padding: 8px; + } + + .page__content p { + margin: 0 0 0px !important; + } + + .page__content p > strong { + font-size: 0.8rem !important; + } + + </style> +</head> + +### 스택(Stack) + +<br/> + + ++ LIFO(후입선출)구조 ++ 배열의 끝에서만 데이터를 접근할 수 있는 선형 자료구조입니다. +데이터의 삽입과 삭제가 한쪽에서만 수행되는 구조입니다. + +#### 리스트를 이용한 Stack + +<br/> + ++ push : 스택의 맨 위에 항목 삽입 ++ pop : 스택의 맨 위 항목을 반환,삭제 ++ top/peek :스택 맨 끝의 항목을 조회 ++ empty : 스택이 비어있는지 확인 ++ size : 스택의 크기를 확인 + + +```python +class Stack(object): + # 생성자 + def __init__(self): + self.items = [] # 값을 담아줄 스택 형태의 리스트 + + # 스택이 비었는지 확인하는 함수(Empty : True / Not Empty : false) + def isEmpty(self): + return not self.items + + # pop : 스택의 맨 끝의 요소 반환, 삭제 + def pop(self): + # 스택이 비어있다면 + if self.isEmpty(): + print("Stack is Empty!") + else: + value = self.items.pop() # 리스트의 가장 마지막 요소 반환, 삭제 후 value에 저장 + return value + + # push : 스택의 맨 끝에 요소 삽입 + def push(self, value): + self.items.append(value) + + # size : 스택의 크기 조회 + def size(self): + return len(self.items) + + # peek / top : 스택의 맨 위의 항목 조회 + def peek(self): + if self.isEmpty(): + print("Stack is Empty!") + else: + return self.items[-1] # 리스트의 마지막 값 + + def __repr__(self): + return repr(self.items) +``` + +```python +if __name__ == "__main__": + stack = Stack() + print("스택이 비었습니까 ? {}".format(stack.isEmpty())) + print("스택에 값을 추가합니다.") + for i in range(10): + stack.push(i) + print("Stack : {}".format(stack)) + print("스택의 크기 : {}".format(stack.size())) + print("peek : {}".format(stack.peek())) + print("pop : {}".format(stack.pop())) + print("peek : {}".format(stack.peek())) + print("Stack : {}".format(stack)) +``` + + 스택이 비었습니까 ? True + 스택에 값을 추가합니다. + Stack : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + 스택의 크기 : 10 + peek : 9 + pop : 9 + peek : 8 + Stack : [0, 1, 2, 3, 4, 5, 6, 7, 8] + + + + 객체에 대한 정보를 프린트 구문으로 출력하게 만들어주는 메소드 + (당연히 print구문으로 객체들을 호출하면 객체의 주소값이 출력됨 -> __repr__, __str__ 메소드 활용) + + <br/> + + + __repr__ : 객체의 official(공식적인) 정보를 출력 + -> 시스템이 인식하는 대로, 객체의 모습 그대로를 호출하는 "딱딱한" 호출 + + __str__ : 객체의 informal(비공식적인) 정보를 출력 + -> 사용자 입장에서 보기 쉬운, 조금은 "느슨한" 호출 + +#### 노드를 이용한 Stack 구현 + +<br/> + ++ 노드의 기본 모양을 먼저 정의합니다. + + +```python +# 노드의 기본 모양 +class Node(object): + def __init__(self, value = None, pointer = None): # value와 pointer 초기화 + self.value = value + self.pointer = pointer +``` + ++ 정의된 노드를 활용하여 스택을 구현합니다. + + +```python +# 스택 구현 +class Stack(object): + def __init__(self): + self.top = None # 초기에 스택은 비어있으므로 top, count 초기화 + self.count = 0 + + def isEmpty(self): + return not bool(self.top) # 스택의 Top이 비어있으면 Empty + + def pop(self): + # 스택이 비어있다면 + if self.isEmpty(): + print("Stack is Empty!") + else: + node = self.top # 임시 node에 스택의 Top부터 담는다 + self.top = self.top.pointer # node의 다음 가리키는 곳을 Top으로 지정 + self.count -= 1 # count 감소 + + return node.value # node의 값 반환(기존의 top의 값) + + def push(self, item): + self.top = Node(item, self.top) # 새로운 값과 기존의 Top을 포인터로 Node형식(값, 포인터) + self.count += 1 # count 증가 + + def size(self): + return self.count + + def peek(self): + if self.isEmpty(): + print("Stack is Empty!") + else: + return self.top.value + + def printStack(self): + node = self.top + while node: + print(node.value, end=" ") + node = node.pointer + print() +``` +```python +if __name__ == "__main__": + stack = Stack() + print("스택이 비었습니까? {}".format(stack.isEmpty())) + print("스택에 값을 추가합니다.") + for i in range(10): + stack.push(i) + stack.printStack() + print("스택의 크기 : {}".format(stack.size())) + print("peek : {}".format(stack.peek())) + print("pop {}".format(stack.pop())) + print("peek : {}".format(stack.peek())) + stack.printStack() +``` + + 스택이 비었습니까? True + 스택에 값을 추가합니다. + 9 8 7 6 5 4 3 2 1 0 + 스택의 크기 : 10 + peek : 9 + pop 9 + peek : 8 + 8 7 6 5 4 3 2 1 0 + diff --git "a/_posts/2022-01-13-1.\354\204\240\355\230\225\355\232\214\352\267\200\354\231\200 \352\262\275\354\202\254\355\225\230\352\260\225\353\262\225.md" "b/_posts/2022-01-13-1.\354\204\240\355\230\225\355\232\214\352\267\200\354\231\200 \352\262\275\354\202\254\355\225\230\352\260\225\353\262\225.md" new file mode 100644 index 000000000000..8d385654e41b --- /dev/null +++ "b/_posts/2022-01-13-1.\354\204\240\355\230\225\355\232\214\352\267\200\354\231\200 \352\262\275\354\202\254\355\225\230\352\260\225\353\262\225.md" @@ -0,0 +1,647 @@ +--- +layout: single +title: "선형 회귀와 경사 하강법 정리" +categories: 딥러닝 +tag: [python, Numpy, 파이썬, 머신러닝, 딥러닝, 기초통계, 선형회귀, 경사하강법] +toc: true +toc_sticky: true + +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 8px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + + .output_prompt { + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; + -webkit-overflow-scrolling: touch; + padding: 0.8rem; + margin-top: 0; + margin-bottom: 15px; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: $code-text-color; + border: solid 1px $border-color; + border-radius: 0.3rem; + word-break: normal; + white-space: pre; + } + + .dataframe tbody tr th:only-of-type { + vertical-align: middle; + } + + .dataframe tbody tr th { + vertical-align: top; + } + + .dataframe thead th { + text-align: center !important; + padding: 8px; + } + + .page__content p { + margin: 0 0 0px !important; + } + + .page__content p > strong { + font-size: 0.8rem !important; + } + + </style> +</head> + + +저는 이 책을 참고하여 개념을 정리했습니다. + + + +<br/> + + + + + + +### 선형 회귀와 로지스틱 회귀 + + + + 선형 회귀는 (x, y) 점들의 특징을 가장 잘 나타내는 선을 그리는 과정입니다. + + + +- 독립변수 x 하나의 값만으로 y값을 설명할 수 있다면 단순 선형 회귀 + + + +<br/> + + + +- 여러개의 x값이 필요할 땐 다중 선형 회귀 + + +#### 최소 제곱법 + + 최소 제곱법은 주어진 x의 값이 하나일 때 적용이 가능합니다. (단순 선형 회귀일 때) + + 여러개의 x가 주어지는 경우(다중 선형 회귀) 경사하강법을 적용합니다. + +<br/> + + + + + + + +<br/> + + + +##### 최소제곱법을 이용하여 a, b 구하기 + + 구하려는 직선이 y = ax + b라고 가정한다면, + + + + a = (x - x평균) * (y - y평균)의 합 / (x - x평균)^2의 합 + + + + b = y - ax이므로 + + = (y의 평균) - (a * x의 평균) + + + +```python +import numpy as np + +x = [2, 4, 6, 8] +y = [81, 93, 91, 97] + +mx = np.mean(x) # x의 평균 +my = np.mean(y) # y의 평균 + +# 기울기 공식의 분모 +divisor = sum([(i - mx)**2 for i in x]) + +# 기울기 공식의 분자 +def top(x, y, mx, my): + d = 0 + + for i in range(len(x)): + d += (x[i] - mx) * (y[i] - my) + + return d +divided = top(x, y, mx, my) + +a = divided / divisor +b = my - mx * a + +print(f"기울기 : {a}") +print(f"y절편 : {b}") +``` + +<pre> +기울기 : 2.3 +y절편 : 79.0 +</pre> + 하지만, 모든 딥러닝 프로젝트는 여러개의 입력변수를 다룹니다. + + + + 따라서 최소제곱법이 아닌 다른 방법이 필요한데, 가장 많이 사용하는 방법은 + + '일단 그리고 조금씩 수정해 나가기' 방식입니다. + + + + 즉, 나중에 그린선이 먼저 그린 선보다 더 좋은지 나쁜지를 판단해야 하는데, + + 각 선의 오차를 계산하여 오차가 작은 쪽으로 바꾸는 알고리즘이 필요합니다. + + +#### 평균 제곱 오차(MSE) + + 여러개의 x에 대해 오차가 존재하므로 모든 x에 대한 평균 오차를 평균 제곱 오차라고 합니다. + + + +<br/> + + + + + + + +<br/> + + + + 따라서, 선형 회귀란 임의의 직선을 그어 이에 대한 평균 제곱 오차(MSE)를 구하고, + + 이 값을 가장 작게 만들어 주는 a와 b 값을 찾아가는 작업입니다. + + + +```python +# 위의 y = ax + b 활용 + +fake_a_b = [a, b] + +# 위에서 구한 직선으로 y값 예측 +def predict(x): + return fake_a_b[0] * x + fake_a_b[1] + +# MSE 함수 +def mse(y, y_hat): # y_hat: y의 평균 + return ((y - y_hat)**2).mean() + +# MSE 함수를 각 y값에 대입하여 최종 값을 구하는 함수 +def mse_val(y, predict_result): + + # 리스트의 각 원소끼리 -연산을 하기 위해 np.array형태 + return mse(np.array(y), np.array(predict_result)) + +# 예측값이 들어갈 리스트 +predict_result = [] + +# 예측값 구하기 +for i in range(len(x)): + predict_result.append(predict(x[i])) + print(f"x값 : {x[i]}, 실제 y값 : {y[i]}, 예측 y값 : {predict_result[i]}") + +# 최종 MSE 출력 +print(f"MSE : {mse_val(predict_result, y)}") +``` + +<pre> +x값 : 2, 실제 y값 : 81, 예측 y값 : 83.6 +x값 : 4, 실제 y값 : 93, 예측 y값 : 88.2 +x값 : 6, 실제 y값 : 91, 예측 y값 : 92.8 +x값 : 8, 실제 y값 : 97, 예측 y값 : 97.4 +MSE : 8.299999999999985 +</pre> +#### 오차 수정하기 + + 기울기 a를 너무 크게 잡거나 너무 작게 잡으면 오차가 커집니다. + + 이러한 기울기 a와 오차의 관계는 이차 함수 그래프로 표현할 수 있습니다. + + + +<br/> + + + + + + + +<br/> + + + + 오차가 가장 작을 때는 아래쪽의 볼록한 부분 m일때 입니다. + + + + 컴퓨터를 이용해 m의 값을 구하려면 임의의 한점(a1)을 찍고, + + 이점을 m에 가까운 쪽으로 점점 이동(a1 -> a2)시키는 과정이 필요합니다. + + + + 이러한 방법을 미분 기울기를 이용하는 경사 하강법이라고 합니다. + + + +##### 경사 하강법 + + 최솟값 m에서의 순간 기울기는 0입니다. + + 즉, 경사 하강법에서는 미분값이 0인 지점을 찾는 것이 됩니다. + + + ++ 경사 하강법의 과정 + + 1. a1에서 미분을 구합니다. + + 2. 구해진 기울기의 반대 방향(기울기가 +면 음의 방향, -면 양의 방향)으로 + + 이동시킨 a2에서 미분을 구합니다. + + 3. 구한 미분 값이 0이 될때까지 반복합니다. + + + +<br/> + + + + + + + +##### 학습률 + + 기울기의 부호를 바꿔 이동시킬 때 적절한 거리를 찾지 못해 너무 멀리 이동시키면 + + a 값이 한점으로 모이지 않고 위로 치솟아 버립니다. + + + + 따라서 어느 만큼 이동시킬지를 신중히 결정해야 하는데, 이때 이동 거리를 정해주는 것이 바로 학습률입니다. + + + + 딥러닝에서 학습률의 값을 적절히 바꾸면서 최적의 학습률을 찾는 것은 중요한 최적화 과정 중 하나입니다. + + + +<br/> + + + ++ 따라서, 경사하강법은 오차의 변화에 따라 이차 함수 그래프를 만들고 적절한 학습률을 설정해 + +미분 값이 0인 지점을 구하는 것입니다. + + + +<br/> + + + ++ y절편 b값(편향)도 이와 같은 성질을 가지고 있기 때문에 최적의 b값을 구할 때 역시 경사하강법을 사용합니다. + + +##### 경사 하강법 실습 + + + +```python +import pandas as pd +import matplotlib.pyplot as plt + +# 그래프로 나타내기 +plt.figure(figsize=(8, 5)) +plt.scatter(x, y) +plt.show() + +x_data = np.array(x) +y_data = np.array(y) + +# 기울기 a와 절편 b의 값 초기화 +a, b = 0, 0 + +lr = 0.03 # 적절한 학습률 정하기 +epochs = 2001 # 몇 번 반복할지 설정 + +# 경사 하강법 시작 +for i in range(epochs): + y_pred = a * x_data + b # y예측값 구하기 + error = y_data - y_pred + + # 오차 함수를 a로 미분한 값 + a_diff = -(2 / len(x_data)) * sum(x_data * error) + + # 오차 함수를 b로 미분한 값 + b_diff = -(2 / len(x_data)) * sum(error) + + a = a - lr * a_diff + b = b - lr * b_diff + + if i % 100 == 0: # 100번째 반복때마다 출력 + print(f"epochs = {i}, 기울기 = {a}, 절편 = {b}") + +# 그래프 다시 그리기 +y_pred = a * x_data + b +plt.scatter(x, y) +plt.plot([min(x_data), max(x_data)], [min(y_pred), max(y_pred)]) +plt.show() +``` + +<img src="data:image/png;base64,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"/> + +<pre> +epochs = 0, 기울기 = 27.84, 절편 = 5.43 +epochs = 100, 기울기 = 7.073858435872394, 절편 = 50.51166161138297 +epochs = 200, 기울기 = 4.095999380762421, 절편 = 68.28224379060177 +epochs = 300, 기울기 = 2.9756829100119027, 절편 = 74.96781336233505 +epochs = 400, 기울기 = 2.5542024233262106, 절편 = 77.48302865233052 +epochs = 500, 기울기 = 2.3956349066513707, 절편 = 78.42929177728175 +epochs = 600, 기울기 = 2.3359793398132864, 절편 = 78.78529068727728 +epochs = 700, 기울기 = 2.313535987420573, 절편 = 78.91922301600925 +epochs = 800, 기울기 = 2.3050924490666174, 절편 = 78.96961044185792 +epochs = 900, 기울기 = 2.3019158585694823, 절편 = 78.98856697541248 +epochs = 1000, 기울기 = 2.3007207758016315, 절편 = 78.99569871827002 +epochs = 1100, 기울기 = 2.300271167070735, 절편 = 78.99838179089187 +epochs = 1200, 기울기 = 2.3001020172709508, 절편 = 78.9993912045567 +epochs = 1300, 기울기 = 2.300038380484562, 절편 = 78.99977096168232 +epochs = 1400, 기울기 = 2.3000144393354365, 절편 = 78.9999138322214 +epochs = 1500, 기울기 = 2.3000054323026444, 절편 = 78.99996758234106 +epochs = 1600, 기울기 = 2.300002043716771, 절편 = 78.99998780397235 +epochs = 1700, 기울기 = 2.3000007688780433, 절편 = 78.99999541166466 +epochs = 1800, 기울기 = 2.300000289263884, 절편 = 78.99999827379686 +epochs = 1900, 기울기 = 2.3000001088255795, 절편 = 78.9999993505755 +epochs = 2000, 기울기 = 2.3000000409418653, 절편 = 78.99999975567644 +</pre> +<img src="data:image/png;base64,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"/> + +##### 학습률 실습 + + + +```python +# 경사하강법 구현 +# 학습률을 여러가지 설정합니다. +lr_list = [0.001, 0.1, 0.5, 0.9] + +def get_derivative(lr): + w_old = 2 + derivative = [w_old] + + y = [w_old ** 2] # 손실함수를 y = w^2 + + for i in range(10): # 미분값 구하기 + dev_value = w_old * 2 + + # 미분값을 이용해서 가중치 업그레이드 + w_new = w_old - lr * dev_value + w_old = w_new + + derivative.append(w_old) # 업데이트한 가중치를 저장 + y.append(w_old ** 2) # 업데이트한 가중치의 손실값을 저장 + + return derivative, y + +x = np.linspace(-2, 2, 50) # -2 ~ 2까지 50구간으로 나누기 +x_square = [i ** 2 for i in x] + +fig = plt.figure(figsize=(12, 7)) + +for i, lr in enumerate(lr_list): + derivative, y = get_derivative(lr) + ax = fig.add_subplot(2, 2, i + 1) + ax.scatter(derivative, y, color = 'red') + ax.plot(x, x_square) + ax.title.set_text('lr = '+str(lr)) + +plt.show() +``` + +<img src="data:image/png;base64,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"/> + +#### 다중 선형 회귀 + + + + 여러 독립 변수(x)가 존재하는 선형 회귀입니다. + + + +```python +data = [[2, 0, 81], [4, 4, 93], [6, 2, 91], [8, 3, 97]] # [x1, x2, y] + +# data로 부터 x1, x2, y의 리스트 만들기 +x1 = [i[0] for i in data] +x2 = [i[1] for i in data] +y = [i[2] for i in data] + +# 그래프로 확인(점 찍어보기) +ax = plt.axes(projection='3d') # 3차원 그래프 +ax.set_xlabel('x1') +ax.set_ylabel('x2') +ax.set_zlabel('y') +ax.dist = 11 +ax.scatter(x1, x2, y) +plt.show() + +# 리스트 -> numpy 배열 +x1_data = np.array(x1) +x2_data = np.array(x2) +y_data = np.array(y) + +a1, a2, b = 0, 0, 0 # 기울기 a1, a2와 절편 b 초기화 +lr = 0.05 # 학습률 +epochs = 2001 # 반복 횟수 + +for i in range(epochs): # epochs만큼 반복 + y_pred = a1 * x1_data + a2 * x2_data + b + error = y_data - y_pred # 예측값에 대한 오차 + + # 미분 + a1_diff = -(1 / len(x1_data)) * sum(x1_data * error) + a2_diff = -(1 / len(x2_data)) * sum(x2_data * error) + b_diff = -(1 / len(x1_data)) * sum(error) + + a1 = a1 - lr * a1_diff # 학습률을 곱해 a1, a2, b값 업데이트 + a2 = a2 - lr * a2_diff + b = b - lr * b_diff + + if i % 100 == 0: + print(f"epochs = {i}, a1 = {a1}, a2 = {a2}, b = {b}") +``` + +<img src="data:image/png;base64,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"/> + +<pre> +epochs = 0, a1 = 23.200000000000003, a2 = 10.5625, b = 4.525 +epochs = 100, a1 = 6.434805446608685, a2 = 3.98925524892891, b = 43.97569356791496 +epochs = 200, a1 = 3.7255348759847924, a2 = 3.0541408134809784, b = 62.576629969168025 +epochs = 300, a1 = 2.503714679287745, a2 = 2.6322742530634486, b = 70.96564181265175 +epochs = 400, a1 = 1.952674645725859, a2 = 2.4420125981800993, b = 74.74908051776274 +epochs = 500, a1 = 1.7041559609856545, a2 = 2.356204719590433, b = 76.45540883097016 +epochs = 600, a1 = 1.592074201194008, a2 = 2.31750542401674, b = 77.22496175342586 +epochs = 700, a1 = 1.5415254028566499, a2 = 2.300052067722291, b = 77.57202960424704 +epochs = 800, a1 = 1.5187279287796718, a2 = 2.292180615763661, b = 77.7285569717077 +epochs = 900, a1 = 1.5084462832928398, a2 = 2.28863059622062, b = 77.79915070923249 +epochs = 1000, a1 = 1.5038092680884374, a2 = 2.2870295397926297, b = 77.83098843741102 +epochs = 1100, a1 = 1.5017179773477205, a2 = 2.286307464419652, b = 77.84534723142188 +epochs = 1200, a1 = 1.500774806629192, a2 = 2.285981808911738, b = 77.85182303787037 +epochs = 1300, a1 = 1.5003494372690325, a2 = 2.28583493849654, b = 77.85474362222634 +epochs = 1400, a1 = 1.5001575959734832, a2 = 2.2857687000379943, b = 77.85606080380799 +epochs = 1500, a1 = 1.5000710756781244, a2 = 2.285738826537708, b = 77.85665485181606 +epochs = 1600, a1 = 1.5000320550830635, a2 = 2.2857253536091546, b = 77.85692276706845 +epochs = 1700, a1 = 1.500014456820917, a2 = 2.2857192773274324, b = 77.85704359666722 +epochs = 1800, a1 = 1.5000065200165227, a2 = 2.285716536928462, b = 77.85709809073502 +epochs = 1900, a1 = 1.500002940523069, a2 = 2.2857153010103657, b = 77.85712266752303 +epochs = 2000, a1 = 1.5000013261739265, a2 = 2.2857147436121346, b = 77.85713375163787 +</pre> +#### 로지스틱 회귀 + ++ 참과 거짓(예/ 아니오)중에 하나를 내놓는 과정에서 사용하는 원리입니다. + + + +<br/> + + + ++ 참(1)과 거짓(0)사이를 구분하는 S자 형태입니다. + + +#### Sigmoid 함수 + + Sigmoid를 이용해 로지스틱 회귀를 풀어나갑니다. + + + +<br/> + + + + + + + +<br/> + + + + 지수부분이 (a * x + b)라고 했을 때, + + + + a는 그래프의 경사도를 결정합니다. a값이 커지면 경사가 커지고 a값이 작아지면 경사가 작아집니다. + + + + b는 그래의 좌우 이동을 의미합니다. b값이 크면 왼쪽, 작으면 오른쪽으로 이동합니다. + + + + 따라서, a와 b의 값에 따라 오차가 변합니다. + + + +```python +# sigmoid 구현 + +x_data = [2, 4, 6, 8, 10, 12, 14] +y_data = [0, 0, 0, 1, 1, 1, 1] + +# 그래프로에 점 찍어보기 +plt.scatter(x_data, y_data) +plt.xlim(0, 15) +plt.ylim(-0.1, 1.1) + +a, b = 0, 0 # 기울기, 절편 초기화 +lr = 0.05 # 학습률 + +def sigmoid(x): + return 1 / (1 + np.e**(-x)) + +# 경사 하강법 실행 +for epoch in range(2001): + for i in range(len(x_data)): + # 미분 오차 : 예측값 - 실제값 + a_diff = x_data[i] * (sigmoid(a * x_data[i] + b) - y_data[i]) + b_diff = sigmoid(a * x_data[i] + b) - y_data[i] + + # a, b값 업데이트 + a -= lr * a_diff + b -= lr * b_diff + + if epoch % 1000 == 0: + print(f"epoch = {epoch}, 기울기 a = {a}, 절편 b = {b}") +``` + +<pre> +epoch = 0, 기울기 a = -0.05, 절편 b = -0.025 +epoch = 0, 기울기 a = -0.13879722189107602, 절편 b = -0.047199305472769 +epoch = 0, 기울기 a = -0.2267516349901771, 절편 b = -0.06185837432261918 +epoch = 0, 기울기 a = 0.12010284223274431, 절편 b = -0.018501564669754007 +epoch = 0, 기울기 a = 0.23740192458962306, 절편 b = -0.006771656434066131 +epoch = 0, 기울기 a = 0.27045811205658526, 절편 b = -0.004016974145152613 +epoch = 0, 기울기 a = 0.2860409628939473, 절편 b = -0.0029039133710553225 +epoch = 1000, 기울기 a = 1.497824502953391, 절편 b = -9.940123304992555 +epoch = 1000, 기울기 a = 1.4940419536789271, 절편 b = -9.94106894231117 +epoch = 1000, 기울기 a = 1.4119848217717417, 절편 b = -9.954745130962369 +epoch = 1000, 기울기 a = 1.4949143121842228, 절편 b = -9.944378944660809 +epoch = 1000, 기울기 a = 1.498244938547387, 절편 b = -9.944045882024492 +epoch = 1000, 기울기 a = 1.4984392510394648, 절편 b = -9.94402968931682 +epoch = 1000, 기울기 a = 1.4984505498747405, 절편 b = -9.944028882257157 +epoch = 2000, 기울기 a = 1.9064689962225458, 절편 b = -12.948894836795326 +epoch = 2000, 기울기 a = 1.9054981310086199, 절편 b = -12.949137553098806 +epoch = 2000, 기울기 a = 1.8514893310246738, 절편 b = -12.958139019762797 +epoch = 2000, 기울기 a = 1.9056614126778697, 절편 b = -12.951367509556148 +epoch = 2000, 기울기 a = 1.9067744947792462, 절편 b = -12.95125620134601 +epoch = 2000, 기울기 a = 1.9068037073321078, 절편 b = -12.951253766966605 +epoch = 2000, 기울기 a = 1.9068044592233457, 절편 b = -12.951253713260089 +</pre> +<img src="data:image/png;base64,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"/> diff --git "a/_posts/2022-01-13-2.\353\213\244\354\270\265 \355\215\274\354\205\211\355\212\270\353\241\240\352\263\274 \355\231\234\354\204\261\355\231\224 \355\225\250\354\210\230.md" "b/_posts/2022-01-13-2.\353\213\244\354\270\265 \355\215\274\354\205\211\355\212\270\353\241\240\352\263\274 \355\231\234\354\204\261\355\231\224 \355\225\250\354\210\230.md" new file mode 100644 index 000000000000..c6c08b643cdf --- /dev/null +++ "b/_posts/2022-01-13-2.\353\213\244\354\270\265 \355\215\274\354\205\211\355\212\270\353\241\240\352\263\274 \355\231\234\354\204\261\355\231\224 \355\225\250\354\210\230.md" @@ -0,0 +1,313 @@ +--- +layout: single +title: "다층 퍼셉트론(XOR문제)과 활성화 함수" +categories: 딥러닝 +tag: [python, Numpy, 파이썬, 머신러닝, 딥러닝, 기초통계, XOR, 오차 역전파, 활성화 함수] +toc: true +toc_sticky: true + +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 8px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + + .output_prompt { + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; + -webkit-overflow-scrolling: touch; + padding: 0.8rem; + margin-top: 0; + margin-bottom: 15px; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: $code-text-color; + border: solid 1px $border-color; + border-radius: 0.3rem; + word-break: normal; + white-space: pre; + } + + .dataframe tbody tr th:only-of-type { + vertical-align: middle; + } + + .dataframe tbody tr th { + vertical-align: top; + } + + .dataframe thead th { + text-align: center !important; + padding: 8px; + } + + .page__content p { + margin: 0 0 0px !important; + } + + .page__content p > strong { + font-size: 0.8rem !important; + } + + </style> +</head> + + +저는 이 책을 참고하여 개념을 정리했습니다. + + + +<br/> + + + + + + +#### 다층 퍼셉트론으로 XOR 문제 해결 + + + ++ OR게이트와 NAND게이트, 이 두 가지를 내재한 각각의 퍼셉트론과 + + AND게이트를 내제한 퍼셉트론을 사용해서 XOR문제를 해결합니다. + + + +```python +import numpy as np + +w11 = np.array([-2, -2]) # NAND 게이트에 입력되는 가중치와 편향 +b1 = 3 +w12 = np.array([2, 2]) # OR +b2 = -1 +w2 = np.array([1, 1]) # AND +b3 = -1 + +# 퍼셉트론 함수 +def MLP(x, w, b): + y = np.sum(w * x) + b + if y <= 0: + return 0 + else: + return 1 + +def NAND(x1, x2): # NAND 게이트 + return MLP(np.array([x1, x2]), w11, b1) + +def OR(x1, x2): # OR 게이트 + return MLP(np.array([x1,x2]), w12, b2) + +def AND(x1, x2): # AND 게이트 + return MLP(np.array([x1, x2]), w2, b3) + +def XOR(x1, x2): + return AND(NAND(x1, x2), OR(x1, x2)) + +if __name__ == "__main__": + for x1, x2 in [(0, 0), (0, 1), (1, 0), (1, 1)]: + y = XOR(x1, x2) + print(f"입력값 : {(x1, x2)}, 출력값 : {y}") +``` + +<pre> +입력값 : (0, 0), 출력값 : 0 +입력값 : (0, 1), 출력값 : 1 +입력값 : (1, 0), 출력값 : 1 +입력값 : (1, 1), 출력값 : 0 +</pre> ++ 퍼셉트론 하나로 해결되지 않던 문제를 은닉층을 만들어 해결했습니다. + ++ 은닉층을 여러개 쌓아올려 복잡한 문제를 해결하는 과정을 '신경망'이라고 부릅니다. + + +#### 오차 역전파 + ++ 다층 퍼셉트론에서 결과값을 얻으면 오차를 구해 이를 토대로 앞선 가중치를 차례로 거슬러 올라가며 조정하는 작업 + + + +<br/> + + + +#### 오차 역전파 과정 + + + ++ 1. 결과 값과 실제 값을 비교하여 오차를 구합니다. + ++ 2. 경사 하강법을 이용해 바로 앞 가중치를 오차가 작아지는 방향으로 업데이트 합니다. + ++ 3. 위 과정을 더이상 오차가 줄어들지 않을 때까지 반복합니다. + + + +<br/> + + + +'오차가 작아지는 방향으로 업데이트한다'는 의미는 미분 값이 0에 가까워지는 방향으로 나아간다는 말입니다. + +가중치 수정 작업은 현 가중치에서 가중치에 대한 기울기를 뺀 값(즉, 미분 값)을 빼서 새 가중치로 조정합니다. + + + + + + + +<br/> + + + +오차 역전파 과정에서 '체인룰'을 활용하여 미분 값을 얻어낼 수 있습니다. + + + + + + + +<br/> + + + + + + + +#### 기울기 소실 문제 + + + +<br/> + + + +은닉층이 늘어나면서 역전파를 통해 전달되는 기울기의 값이 점점 작아져 + +맨 처음 층까지 전달되지 않는 문제입니다. + +이는 활성화 함수로 사용된 시그모이드 함수의 특성이 원인입니다. + + + +<br/> + + + + + + + +위 그림과 같이 시그모이드를 미분하면 최대값이 약 0.3이 됩니다. + +1보다 작으므로 계속 곱하다 보면 0에 가까워지게 됩니다. + +따라서, 여러 층을 거칠수록 기울기가 사라져 가중치를 수정하기가 어려워집니다. + +이를 해결하고자 여러 활성화 함수로 대체합니다. + + +#### 고급 경사 하강법 + + + +가중치를 업데이트하는 방법으로 경사 하강법이 존재합니다. + +그런데 경사 하강법은 정확하게 가중치를 찾아가지만, 한 번 업데이트할 때마다 전체 데이터를 + +미분해야 하므로 계산량이 많아 속도가 느리고 최적해를 찾기 전에 멈출수 있다는 단점이 있습니다. + +이러한 점을 보완하기 위해 고급 경사 하강법이 등장합니다. + + + +<br/> + + + +##### 확률적 경사 하강법(SGD) + +확률적 경사 하강법은 전체 데이터를 사용하는 것이 아니라, 랜덤하게 추출한 일부 데이터를 사용합니다. + +그러므로 더 빨리, 더 자주 업데이트를 하는 것이 가능합니다. + + + + + + + +그림을 참고한다면, 랜덤한 일부 데이터를 사용하는 만큼 확률적 경사 하강법은 + +중간 결과의 진폭이 크고 불안정해 보일 수 있습니다. + +하지만 속도가 확연히 빠르면서도 최적 해에 근사한 값을 찾아낼 수 있어 경사 하강법의 대안으로 사용되고 있습니다. + + + +<br/> + + + +##### 모멘텀(momentum) + +momentum이란 '관성, 탄력, 가속도'라는 뜻입니다. 모멘텀 SGD란 말 그대로 경사 하강법에 탄력을 더한 것입니다. + +경사 하강법과 마찬가지로 매번 기울기를 구하지만, 이를 통해 오차를 수정하기 전 바로 앞 수정값과 + +방향(+ / -)을 참고하여 같은 방향으로 일정한 비율만 수정되게 하는 방법입니다. + +따라서 지그재그로 일어나느 현상이 줄어들고, 이전 이동 값을 고려해 일정 비율만큼만 다시 값을 결정하므로 + +관성 효과를 낼 수 있습니다. + + + + + + + +<br/> + + + +##### 고급 경사 하강법 활용법 + + + + + + + +아담(Adam)은 현재 가장 많이 사용되는 고급 경사 하강법입니다. + diff --git "a/_posts/2022-01-25-CNN(Convolution\354\270\265 + Pooling\354\270\265).md" "b/_posts/2022-01-25-CNN(Convolution\354\270\265 + Pooling\354\270\265).md" new file mode 100644 index 000000000000..6ef7bc508bc5 --- /dev/null +++ "b/_posts/2022-01-25-CNN(Convolution\354\270\265 + Pooling\354\270\265).md" @@ -0,0 +1,420 @@ +--- +layout: single +title: "CNN 정리(컨볼루션 층, 풀링 층)" +categories: 딥러닝 +tag: [python, keras, CNN, 컨볼루션, 풀링, Convolution, Pooling, 인공지능, 딥러닝, 머신러닝] +toc: true +toc_sticky: true + +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 8px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + + .output_prompt { + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; + -webkit-overflow-scrolling: touch; + padding: 0.8rem; + margin-top: 0; + margin-bottom: 15px; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: $code-text-color; + border: solid 1px $border-color; + border-radius: 0.3rem; + word-break: normal; + white-space: pre; + } + + .dataframe tbody tr th:only-of-type { + vertical-align: middle; + } + + .dataframe tbody tr th { + vertical-align: top; + } + + .dataframe thead th { + text-align: center !important; + padding: 8px; + } + + .page__content p { + margin: 0 0 0px !important; + } + + .page__content p > strong { + font-size: 0.8rem !important; + } + + </style> +</head> + + +#### 합성곱 신경망 + +합성곱 신경망(Convolutional Neural Network)은 이미지 처리에 탁월한 성능을 보이는 신경망입니다. + +합성곱 신경망은 크게 합성곱층(Convolution layer)과 풀링층(Pooling layer)으로 구성됩니다. + + + +<br/> + + + +이미지 처리를 하기 위해 앞서 배운 다층 퍼셉트론을 사용할 수는 있지만 한계가 있습니다. + +다층 퍼셉트론을 사용한다면 이미지를 1차원 벡터로 변환하고 입력층으로 사용해야 합니다. + +하지만, 몇가지 픽셀값만 달라져도 예측에 적지 않은 영향을 받게 되고, + +1차원 벡터로 변환하면서 공간적인 구조 정보가 유실되므로 좀 더 정확한 예측을 위해 합성곱 신경망을 사용합니다. + + + +<br/> + + + +##### 채널 + +이미지는 (높이, 너비, 채널)이라는 3차원 텐서입니다. + +높이는 이미지의 세로 방향 픽셀 수, 너비는 이미지의 가로 방향 픽셀 수, 채널은 색 성분을 의미합니다. + +여기서 흑백 이미지는 채널 수가 1이고, + +컬러 이미지는 적색(Red), 녹색(Green), 청색(Blue) 세가지 색깔의 조합으로 이루어지므로 채널수가 3입니다. + + + +<br/> + + + +##### 필터(=커널 =가중치) + +합성곱층은 합성곱 연산을 통해 이미지의 특징을 추출하는 역할을 합니다. + +이때, 필터(커널)라는 N x M 크기의 행렬로 이미지를 처음부터 끝까지(가장 왼쪽 위부터 가장 오른쪽 아래까지) + +순차적으로 훑으면서 필터와 겹쳐지는 부분의 각 이미지와 필터의 원소(가중치) 값을 곱해서 모두 더한 값을 출력합니다. + + + +<br/> + + + + + +아래의 그림은 채널이 1인(흑백 이미지) 2차원 합성곱일 경우 입력층에 필터를 지나쳐 출력이 되는 예시입니다. + +4x4 크기의 입력층과 2x2 크기의 필터를 합성곱 연산할 경우 3x3형태의 출력이 나옵니다. + +물론 필터에는 각 가중치가 존재하면서 편향도 존재합니다. + +예를 들어, 편향이 +1이라면 아래의 그림에 나오는 출력의 각 원소들은 +1이되어 출력이 됩니다. + +이때, 만들어지는 출력을 특성맵(feature map)이라고 합니다. + +입력층이 필터와 합성곱 연산이 되고 특성맵으로 출력이 될때, 활성화 함수가 사용되는 보통 relu함수를 사용합니다. + + + + + + + +<br/> + + + +##### 여러개의 필터(커널) + +Dense층을 여러개를 사용하는 것처럼 여러개의 필터를 사용할 수 있습니다. + +당연히 필터가 여러개면 필터의 가중치는 서로 다릅니다. + +아래의 그림처럼 4x4크기의 입력에 3개의 필터와 합성곱 연산 결과 3개의 출력 즉, 3x3x3크기의 특성맵이 됩니다. + +그림에서는 필터의 크기가 3x3이었지만, 커널의 크기를 사용자가 정할 수 있습니다. + +다만, 그림에선 커널의 크기가 2x2이지만, 커널의 크기가 크면 특징을 감지하는데 효과가 떨어진다고 하여 보통 3x3 또는 5x5를 사용합니다. + + + +<br/> + + + ++ 커널의 깊이(채널) = 입력 데이터의 깊이(채널) + ++ 합성곱 연산 결과 : 특성 맵은 입력 데이터의 깊이와 상관없이 (높이, 너비)의 특성 맵을 얻습니다. + +(특성 맵의 채널은 1) + ++ 다만, 합성곱 연산에서 다수의 커널을 사용할 경우 : 특성 맵은 (높이, 너비, 커널의 수) 크기가 됩니다. + +(특성 맵의 채널은 합성곱 연산에 사용된 커널의 수) + + + + + + + +<br/> + + + +##### 스트라이드(stride) + +그림에서는 커널이 입력층 위에 한 칸씩 이동(슬라이딩)하면서 합성곱 연산을 수행합니다. + +하지만, 커널의 슬라이딩 범위 또한 사용자가 정할 수 있습니다. + +이러한 이동 범위를 스트라이드(stride)라고 합니다. + +스트라이드가 2일 경우에 4x4 이미지에 2x2의 필터가 두 칸씩 이동하면서 합성곱 연산을 수행하게 됩니다. + +그렇다면, 최종적으로 2x2x3 크기의 특성 맵을 얻게 됩니다. + + + +<br/> + + + +##### 케라스의 합성곱 층 + +```python + +from tensorflow import keras + +keras.layers.Conv2D(10, kernel_size=(3, 3), activation='relu') + +``` + + + ++ 첫번재 인자 10은 필터의 개수를 뜻합니다. + ++ kernel_size는 커널(필터)의 크기를 지정하는 인자입니다. + ++ 역시 activation으로 활성화 함수를 지정합니다. + + + +<br/> + + + +##### 패딩(padding) + ++ 패딩의 목적 + +위 그림에서 4x4 이미지에 2x2 필터로 합성곱 연산을 했을 때, 스트라이드가 1일 경우엔 3x3의 특성 맵을 얻었습니다. + +이처럼 합성곱 연산의 결과로 얻은 특성 맵은 입력보다 크기가 작아진다는 특성이 있습니다. + +그렇다면 합성곱 층을 여러개 쌓은다면 최종적으로 얻는 특성 맵의 크기는 처음 입력보다 매우 작아집니다. + +여러개의 합성곱 연산 이후에도 특성 맵의 크기를 동일하게 유지되도록 하는 작업을 패딩(padding)이라고 합니다. + + + +<br/> + + + +패딩은 입력 이미지의 가장자리에 지정된 개수의 폭만큼 가상의 픽셀로 테두리를 추가합니다. + +주로 값을 0으로 채우는 제로 패딩(zero padding)을 사용합니다. + +그렇다면 필터의 슬라이딩 범위가 늘어나게 되고 최종적으로 얻는 특성 맵의 크기는 패딩하기 전보다 커지게 됩니다. + +아래는 4x4 크기의 이미지를 1폭 만큼 제로 패딩을 사용했을 때 모습입니다. + + + + + +<br/> + + + +##### 케라스의 패딩과 스트라이드 설정 + +```python + +from tensorflow import keras + +keras.layers.Conv2D(10, kernel_size=(3, 3), activation='relu', padding='same', strides=1) + +``` + + + ++ padding='same' : (same 패딩) 입력값과 특성맵 크기를 동일하게 해주는 패딩입니다. + +커널을 사용하여 합성곱 연산을 했을 때, 특성맵의 사이즈가 얼마나 될지 쉽게 알 수 있습니다. + ++ padding='valid' : (valid 패딩) 패딩을 사용하지 않는 옵션입니다. + ++ stride는 특수한 상황이 아닌 이상, 보통 1로 설정합니다. + + +##### 풀링 + +일반적으로 합성곱 층(합성곱 연산 + 활성화 함수) 다음에는 풀링 층을 추가합니다. + +합성곱 층(컨볼루션 층)을 통해 나온 특성 맵이 크고 복잡하다면 이를 축소(다운샘플링)해야 합니다. + +이 과정을 풀링 연산(서브샘플링)이라 하며, 이러한 연산을 하는 층을 풀링 층이라고 합니다. + + + +<br/> + + + +일반적으로 최대 풀링(max pooling)과 평균 풀링(average pooling)이 사용됩니다. + +최대 풀링은 정해진 구역 안에서 최댓값을 뽑아내는 방법이고, + +평균 풀링은 평균값을 뽑아내는 방법입니다. + + + +<br/> + + + +아래의 그림은 4x4크기의 특성 맵을 스트라이드가 2일때, 2x2 커널로 맥스 풀링하는 모습입니다. + + + + + +<br/> + + + +위의 그림에서 특성 맵을 맥스풀링하게 된다면 2x2의 크기, 절반으로 특성 맵이 작아지는 걸 확인할 수 있습니다. + +만약 채널이 추가된다면 즉, 컨볼루션 층이 3개가 사용되어 특성 맵이 4x4x3 크기였다면 맥스 풀링 결과는 2x2x3이 됩니다. + +차원이 여러개 있는 특성 맵에서 풀링은 각 차원마다 수행되므로 채널 차원은 변하지 않습니다. + + + +<br/> + + + ++ 풀링과 합성곱의 차이 + +풀링도 합성곱 연산과 유사하게 커널을 슬라이딩시켜 특성 맵을 얻으므로 유사하다고 느낄 수 있지만, + +풀링은 합성곱 연산과 달리 곱하기나(가중치) 더하는(편향) 연산이 없는 걸 확인할 수 있습니다. + +또한, 풀링 과정에서 커널이 슬라이딩할 때 서로 겹치지 않습니다. + +즉, 위의 사진에서 커널이 2x2라면 스트라이드는 2로 설정되고 겹치지 않게 이동합니다. + +커널이 3x3이면 스트라이드는 3, 커널이 4x4이면 스트라이드는 4가 됩니다. + +하지만, 통상적으로 풀링은 2x2 커널을 사용합니다. + + + +<br/> + + + +##### 케라스의 풀링 층 + +```python + +keras.layers.MaxPooling2D(2, strides=2, padding='valid') + +``` + ++ 첫번째 인자 2는 2x2의 커널로 풀링하겠다는 뜻입니다. + ++ strides는 따로 지정하지 않아도 자동으로 풀링의 크기에 맞춰서 셋팅되기 때문에 굳이 지정하지 않아도 됩니다. + ++ 풀링은 패딩을 하지않고(valid패딩), 입력 크기를 줄이는게 목적이기 때문에 굳이 지정하지 않아도 됩니다. + + + +<br/> + + + +##### 컨볼루션 층 + 풀링 층 사용 + +앞서 확인한 내용들을 바탕으로 컨볼루션 층과 풀링 층은 쌍으로 보통 사용합니다. + +(컨볼루션 층 + 풀링 층) 또는 (컨볼루션 층 + 컨볼루션 층 + 풀링 층) 이런 식으로 사용하게 되는데, + +2차원 이상의 크기로 출력이 됩니다. + +그 후에 밀집층(은닉층+출력층 또는 출력층)을 사용하여 원하는 결과값을 얻기 위해 1차원 배열로 풀어서 펼쳐야 합니다. + +따라서 Flatten()함수를 사용하여 1차원 크기로 바꾸어 사용합니다. + + +#### 3차원 합성곱 + + + +보통 컬러 이미지를 입력 데이터로 사용한다면 3차원 배열(높이, 너비, 깊이(채널)) 크기가 됩니다. + +이러한 경우엔 커널도 동일하게 3차원으로 만들면 됩니다. + +3차원 합성곱에서 커널의 설명은 위에서 설명한 내용과 같습니다. + + + ++ 커널의 깊이(채널) = 입력 데이터의 깊이(채널) + ++ 합성곱 연산 결과 : 특성 맵은 입력 데이터의 깊이와 상관없이 (높이, 너비)의 특성 맵을 얻습니다. + +(특성 맵의 채널은 1) + ++ 다만, 합성곱 연산에서 다수의 커널을 사용할 경우 : 특성 맵은 (높이, 너비, 커널의 수) 크기가 됩니다. + +(특성 맵의 채널은 합성곱 연산에 사용된 커널의 수) + diff --git a/_posts/2022-01-25-Fashion_mnist.md b/_posts/2022-01-25-Fashion_mnist.md new file mode 100644 index 000000000000..4e29023bbe3b --- /dev/null +++ b/_posts/2022-01-25-Fashion_mnist.md @@ -0,0 +1,901 @@ +--- +layout: single +title: "인공신경망 정리, fashion mnist" +categories: 딥러닝 +tag: [python, keras, 파이썬, 딥러닝, fashion mnist, 인공신경망, 다중 분류, 예측] +toc: true +toc_sticky: true + +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 8px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + + .output_prompt { + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; + -webkit-overflow-scrolling: touch; + padding: 0.8rem; + margin-top: 0; + margin-bottom: 15px; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: $code-text-color; + border: solid 1px $border-color; + border-radius: 0.3rem; + word-break: normal; + white-space: pre; + } + + .dataframe tbody tr th:only-of-type { + vertical-align: middle; + } + + .dataframe tbody tr th { + vertical-align: top; + } + + .dataframe thead th { + text-align: center !important; + padding: 8px; + } + + .page__content p { + margin: 0 0 0px !important; + } + + .page__content p > strong { + font-size: 0.8rem !important; + } + + </style> +</head> + + +### Fashion MNIST 실습 + + + +<br/> + + + +케라스의 fashion_mnist 데이터의 카테고리를 인공신경망을 활용해 분류해보려 합니다. + +처음으로 scikit-leran의 로지스틱 회귀를 통해 간단하게 분류 정확도를 체크하고, + +간단한 인공신경망 구축, 은닉층 추가, 옵티마이저 설정과 과적합을 피하기 위한 방법을 추가하며 결과를 확인해보겠습니다. + + + +<br/> + + + +Keras의 fashion_mnist데이터 + + + 10개의 패션 아이템 클래스를 가지고 있습니다. + + + 28 x 28 픽셀의 흑백 이미지로 이루어져 있습니다. + + +##### keras의 fashion mnist 데이터 가져오기 + + + +```python +from tensorflow.keras.datasets import fashion_mnist + +(X_train, y_train), (X_test, y_test) = fashion_mnist.load_data() + +X_train.shape, y_train.shape +``` + +<pre> +((60000, 28, 28), (60000,)) +</pre> +##### 데이터 확인해보기 + + + +```python +import matplotlib.pyplot as plt +import numpy as np + +# 0 ~ 9번째 X데이터 출력 +fig, axs = plt.subplots(1, 10, figsize=(10, 10)) + +for i in range(10): + axs[i].imshow(X_train[i], cmap='gray_r') + axs[i].axis('off') +plt.show() + +# 0 ~ 9번째 Y데이터 출력 +print([y_train[i] for i in range(10)]) + +# 카테고리 번호를 출력해보기(return_counts : 각 카테고리당 몇개씩 들어있는지 확인) +print(np.unique(y_train, return_counts=True)) +``` + +<img src="data:image/png;base64,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"/> + +<pre> +[9, 0, 0, 3, 0, 2, 7, 2, 5, 5] +(array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=uint8), array([6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000])) +</pre> +#### sikit-learn의 로지스틱 회귀로 예측 성능 확인해보기 + ++ 픽셀값은 0 ~ 255까지 이므로 0부터 1사이의 값으로 맞춰주기 위해 X데이터를 255.0으로 나누어줍니다. + ++ 3차원의 X데이터를 1차원으로 펼쳐주기 위해 reshape를 해줍니다. + + + +```python +X_train = X_train / 255.0 +X_test = X_test / 255.0 + +X_train_scaled = X_train.reshape(-1, 28 * 28) +X_test_scaled = X_test.reshape(-1, 28 * 28) + +X_train_scaled.shape, X_test_scaled.shape +``` + +<pre> +((60000, 784), (10000, 784)) +</pre> ++ 로지스틱 회귀를 적용하기 위해 loss 매개변수에 손실 함수로 'log'를 지정합니다. + ++ max_iter 매개변수에 반복횟수로 5를 지정합니다. + ++ 반복 실행시 결과가 동일하게 나오기 위해 난수 초기값을 random_state 매개변수로 지정합니다. + + + +```python +# scikit_learn의 SGDClassifier 클래스를 활용하여 경사하강법을 이용한 로지스틱 회귀를 사용 +from sklearn.linear_model import SGDClassifier +from sklearn.model_selection import cross_validate + +sc = SGDClassifier(loss='log', max_iter=5, random_state=42) + +scores = cross_validate(sc, X_train_scaled, y_train, n_jobs=-1) +print(np.mean(scores['test_score'])) # 훈련데이터에 있어서 약 82프로의 정확도를 얻음 +``` + +<pre> +0.8192833333333333 +</pre> +10개의 클래스를 분류해야하는데 손실함수를 'log'로 사용하는 이유 + ++ SGD는 cross_entropy를 지정하는 곳이 따로 없기 때문에 10개의 클래스를 분류하기 위해 10개의 + ++ 예를 들어, 부츠를 양성, 나머지 9개를 음성으로 분류하여 1개의 모델을 훈련 + ++ 티셔츠를 양성, 나머지 9개를 음성으로 분류하여 1개의 모델을 훈련 -> 이런식으로 10개의 모델을 훈련 + ++ 10개의 계산값이 나오면 softmax함수(이진 분류일땐 sigmoid함수)를 사용하여 확률로 바꿔줍니다. + ++ 이런식으로 이진분류를 다중분류처럼 사용하는 방법을 OVR(One verses Rest)라고 합니다. + + +#### 인공 신경망으로 구현 + ++ 로지스틱 회귀 경우 : 픽셀1 x w1 + 픽셀2 x w2 + ... + 픽셀784 x w784 + b => 10개의 모델 + ++ 이를 인공 신경망으로 구현해봅니다. + + + +```python +# 검증 데이터 0.2 비율로 분리 +from sklearn.model_selection import train_test_split +X_train_scaled, X_val_scaled, y_train, y_val = train_test_split( + X_train_scaled, y_train, test_size=0.2, random_state=42) + +X_train_scaled.shape, y_train.shape, X_val_scaled.shape, y_val.shape +``` + +<pre> +((48000, 784), (48000,), (12000, 784), (12000,)) +</pre> +##### 모델 정의 + ++ Dense 레이어를 사용하여 은닉층과 출력층을 생성합니다. + ++ 은닉층의 활성화 함수에는 자주 쓰이는 'relu'를 사용합니다. + ++ 출력층의 활성화 함수에는 다중분류이므로 'softmax'(이진분류일때는 'sigmoid')를 사용합니다. + + + +##### 모델 컴파일 + ++ 손실 함수 + + + 이진 분류 : binary_crossentropy + + + 다중 분류 : categorical_crossentropy + ++ sparse_categorical_crossentropy란? + + + y데이터의 값은 0 ~ 9 까지의 정수인것을 확인했습니다. + + 이 정수값을 그대로 사용할 순 없고, 출력층에는 10개의 유닛에서 softmax함수값을 거쳐 10개의 확률값이 나옵니다. + + + crossentropy의 공식에 따라 10개의 확률값에 각각 로그를 취하고 타깃값과 곱하게 됩니다. + + (샘플이 티셔츠일 확률 : a1 => -log(a1) x target값, -log(a2) x target값, ...) + + + 여기서, 티셔츠는 첫번째 원소가 1이고 나머지 0(\[1, 0, 0, ... , 0\])인 원 핫 인코딩이 되어있어야지만 + + 첫번째 unit을 제외한 나머지 unit에서의 출력값이 모두 0이 곱해져 상쇄되어 티셔츠에 해당되는 뉴런의 출력값만 손실에 반영됩니다. + + + 하지만 원 핫 인코딩을 사용하지 않고, Y데이터의 정수값 그대로를 사용하려면 sparse_categorical_crossentropy를 사용합니다. + + + + + +```python +from tensorflow.keras.models import Sequential +from tensorflow.keras.layers import Dense + +model = Sequential() + +# 다중분류이므로 softmax 사용 +model.add(Dense(10, activation='softmax', input_shape=(784, ))) + +model.compile(loss='sparse_categorical_crossentropy', + optimizer='adam', + metrics=['acc']) +``` + + +```python +# 모델 학습 +history = model.fit(X_train_scaled, y_train, epochs=15, batch_size=100, + validation_data=(X_val_scaled, y_val)) +``` + +<pre> +Epoch 1/15 +480/480 [==============================] - 1s 1ms/step - loss: 0.7469 - acc: 0.7560 - val_loss: 0.5672 - val_acc: 0.8115 +Epoch 2/15 +480/480 [==============================] - 1s 1ms/step - loss: 0.5187 - acc: 0.8279 - val_loss: 0.5018 - val_acc: 0.8309 +Epoch 3/15 +480/480 [==============================] - 0s 1ms/step - loss: 0.4758 - acc: 0.8400 - val_loss: 0.4818 - val_acc: 0.8371 +Epoch 4/15 +480/480 [==============================] - 0s 1ms/step - loss: 0.4544 - acc: 0.8457 - val_loss: 0.4677 - val_acc: 0.8382 +Epoch 5/15 +480/480 [==============================] - 1s 1ms/step - loss: 0.4401 - acc: 0.8502 - val_loss: 0.4564 - val_acc: 0.8411 +Epoch 6/15 +480/480 [==============================] - 1s 1ms/step - loss: 0.4293 - acc: 0.8547 - val_loss: 0.4435 - val_acc: 0.8470 +Epoch 7/15 +480/480 [==============================] - 1s 1ms/step - loss: 0.4214 - acc: 0.8556 - val_loss: 0.4428 - val_acc: 0.8488 +Epoch 8/15 +480/480 [==============================] - 1s 1ms/step - loss: 0.4149 - acc: 0.8583 - val_loss: 0.4364 - val_acc: 0.8489 +Epoch 9/15 +480/480 [==============================] - 1s 1ms/step - loss: 0.4089 - acc: 0.8605 - val_loss: 0.4265 - val_acc: 0.8509 +Epoch 10/15 +480/480 [==============================] - 1s 1ms/step - loss: 0.4049 - acc: 0.8609 - val_loss: 0.4224 - val_acc: 0.8533 +Epoch 11/15 +480/480 [==============================] - 1s 1ms/step - loss: 0.4026 - acc: 0.8615 - val_loss: 0.4261 - val_acc: 0.8514 +Epoch 12/15 +480/480 [==============================] - 1s 1ms/step - loss: 0.3986 - acc: 0.8623 - val_loss: 0.4190 - val_acc: 0.8552 +Epoch 13/15 +480/480 [==============================] - 1s 1ms/step - loss: 0.3952 - acc: 0.8644 - val_loss: 0.4185 - val_acc: 0.8533 +Epoch 14/15 +480/480 [==============================] - 1s 1ms/step - loss: 0.3925 - acc: 0.8634 - val_loss: 0.4213 - val_acc: 0.8560 +Epoch 15/15 +480/480 [==============================] - 1s 1ms/step - loss: 0.3901 - acc: 0.8659 - val_loss: 0.4155 - val_acc: 0.8560 +</pre> + +```python +# 테스트 데이터를 모델에 적용하여 평가하기 [loss, acc] +model.evaluate(X_test_scaled, y_test) +``` + +<pre> +313/313 [==============================] - 0s 854us/step - loss: 0.4530 - acc: 0.8427 +</pre> +<pre> +[0.45295682549476624, 0.8427000045776367] +</pre> +#### 심층 신경망 + ++ 입력층 784개의 뉴런과 은닉층 100개 뉴런, 출력층 10개 뉴런을 생성해보았습니다. + + + +```python +# 모델을 좀 더 깊게(은닉층 추가) 수정 +model = Sequential() + +model.add(Dense(100, activation='relu', input_shape=(784, ))) # 은닉층 +model.add(Dense(10, activation='softmax')) # 출력층 + +model.compile(loss='sparse_categorical_crossentropy', + optimizer='adam', + metrics=['acc']) +``` + +##### model의 summary() 메서드 + ++ layer의 정보, 각 layer의 출력의 크기, 파라미터의 갯수를 확인할 수 있습니다. + ++ 은닉층의 파라미터 갯수 : 784개의 입력 층과 은닉층의 100개의 뉴런이 있고, 각 뉴런은 b(절편)이 존재하므로 + +100개의 b값이 존재합니다. + +따라서, 784 x 100 + 100 = 78500이 됩니다. + ++ 출력층의 파라미터 갯수 : 은닉층에서 나오는 100개의 출력, 10개의 출력층 뉴런이 있으므로 + +100 x 10 + 10 = 1010이 됩니다. + + + +```python +model.summary() +``` + +<pre> +Model: "sequential_1" +_________________________________________________________________ + Layer (type) Output Shape Param # +================================================================= + dense_1 (Dense) (None, 100) 78500 + + dense_2 (Dense) (None, 10) 1010 + +================================================================= +Total params: 79,510 +Trainable params: 79,510 +Non-trainable params: 0 +_________________________________________________________________ +</pre> + +```python +model.fit(X_train_scaled, y_train, + epochs=15, batch_size=128, validation_data=(X_val_scaled, y_val)) +``` + +<pre> +Epoch 1/15 +375/375 [==============================] - 1s 2ms/step - loss: 0.5933 - acc: 0.7972 - val_loss: 0.4648 - val_acc: 0.8373 +Epoch 2/15 +375/375 [==============================] - 1s 1ms/step - loss: 0.4281 - acc: 0.8506 - val_loss: 0.4153 - val_acc: 0.8555 +Epoch 3/15 +375/375 [==============================] - 1s 1ms/step - loss: 0.3919 - acc: 0.8607 - val_loss: 0.3894 - val_acc: 0.8612 +Epoch 4/15 +375/375 [==============================] - 1s 1ms/step - loss: 0.3631 - acc: 0.8710 - val_loss: 0.3836 - val_acc: 0.8612 +Epoch 5/15 +375/375 [==============================] - 1s 2ms/step - loss: 0.3414 - acc: 0.8799 - val_loss: 0.3521 - val_acc: 0.8702 +Epoch 6/15 +375/375 [==============================] - 1s 2ms/step - loss: 0.3286 - acc: 0.8834 - val_loss: 0.3569 - val_acc: 0.8742 +Epoch 7/15 +375/375 [==============================] - 1s 1ms/step - loss: 0.3144 - acc: 0.8865 - val_loss: 0.3525 - val_acc: 0.8730 +Epoch 8/15 +375/375 [==============================] - 1s 1ms/step - loss: 0.3006 - acc: 0.8908 - val_loss: 0.3457 - val_acc: 0.8777 +Epoch 9/15 +375/375 [==============================] - 1s 2ms/step - loss: 0.2924 - acc: 0.8941 - val_loss: 0.3365 - val_acc: 0.8792 +Epoch 10/15 +375/375 [==============================] - 1s 2ms/step - loss: 0.2800 - acc: 0.8987 - val_loss: 0.3267 - val_acc: 0.8859 +Epoch 11/15 +375/375 [==============================] - 1s 2ms/step - loss: 0.2723 - acc: 0.9005 - val_loss: 0.3283 - val_acc: 0.8832 +Epoch 12/15 +375/375 [==============================] - 1s 2ms/step - loss: 0.2653 - acc: 0.9034 - val_loss: 0.3252 - val_acc: 0.8847 +Epoch 13/15 +375/375 [==============================] - 1s 2ms/step - loss: 0.2559 - acc: 0.9055 - val_loss: 0.3183 - val_acc: 0.8844 +Epoch 14/15 +375/375 [==============================] - 1s 2ms/step - loss: 0.2508 - acc: 0.9078 - val_loss: 0.3135 - val_acc: 0.8886 +Epoch 15/15 +375/375 [==============================] - 1s 2ms/step - loss: 0.2430 - acc: 0.9116 - val_loss: 0.3236 - val_acc: 0.8815 +</pre> +<pre> +<keras.callbacks.History at 0x7f89740a0340> +</pre> + +```python +model.evaluate(X_test_scaled, y_test) +``` + +<pre> +313/313 [==============================] - 0s 714us/step - loss: 0.3513 - acc: 0.8715 +</pre> +<pre> +[0.3512624502182007, 0.8715000152587891] +</pre> +#### Relu함수와 Flatten층 + ++ Relu함수 + +간단하게 말하면 Max함수입니다. + +뉴런의 출력값이 0보다 크면 그대로 출력, 0보다 작으면 0으로 출력해주는 단순한 함수입니다. + + + ++ Flatten층 + +실제로 가중치는 존재하지 않는 층이며, 편의를 위해서 추가하는 층입니다. + +위에서 X데이터를 28x28 -> 784의 1차원 배열로 풀어주는 전처리 과정을 포함했습니다. + +이러한 작업을 Fletten이 대신 해줍니다. + +Flatten의 input_shape에 (28, 28)로 지정해주면 784의 1차원 배열로 전달해줍니다. + +summary()에서 보면 Flatten층의 출력 크기는 784로 나오는것을 확인할 수 있습니다. + + + +```python +# Flatten층 사용하기 위해 reshape을 하지 않아도 됩니다. +# 그렇기 위해 귀찮겠지만 다시 데이터를 다시 로드했습니다. + +(X_train, y_train), (X_test, y_test) = fashion_mnist.load_data() + +X_train = X_train / 255.0 +X_test = X_test / 255.0 +``` + + +```python +from tensorflow.keras.layers import Flatten + +model = Sequential() + +model.add(Flatten(input_shape=(28, 28))) +model.add(Dense(100, activation='relu')) +model.add(Dense(10, activation='softmax')) + +model.compile(loss='sparse_categorical_crossentropy', + optimizer='adam', + metrics=['acc']) +``` + + +```python +model.summary() +``` + +<pre> +Model: "sequential_6" +_________________________________________________________________ + Layer (type) Output Shape Param # +================================================================= + flatten_4 (Flatten) (None, 784) 0 + + dense_11 (Dense) (None, 100) 78500 + + dense_12 (Dense) (None, 10) 1010 + +================================================================= +Total params: 79,510 +Trainable params: 79,510 +Non-trainable params: 0 +_________________________________________________________________ +</pre> + +```python +history = model.fit(X_train, y_train, epochs=20, + batch_size=128, validation_split=0.2) +``` + +<pre> +Epoch 1/20 +375/375 [==============================] - 1s 2ms/step - loss: 0.5939 - acc: 0.8002 - val_loss: 0.4570 - val_acc: 0.8438 +Epoch 2/20 +375/375 [==============================] - 1s 1ms/step - loss: 0.4267 - acc: 0.8518 - val_loss: 0.4048 - val_acc: 0.8581 +Epoch 3/20 +375/375 [==============================] - 1s 2ms/step - loss: 0.3846 - acc: 0.8652 - val_loss: 0.3798 - val_acc: 0.8639 +Epoch 4/20 +375/375 [==============================] - 1s 1ms/step - loss: 0.3614 - acc: 0.8720 - val_loss: 0.3696 - val_acc: 0.8674 +Epoch 5/20 +375/375 [==============================] - 1s 1ms/step - loss: 0.3388 - acc: 0.8785 - val_loss: 0.3745 - val_acc: 0.8658 +Epoch 6/20 +375/375 [==============================] - 1s 2ms/step - loss: 0.3238 - acc: 0.8849 - val_loss: 0.3453 - val_acc: 0.8755 +Epoch 7/20 +375/375 [==============================] - 1s 2ms/step - loss: 0.3120 - acc: 0.8859 - val_loss: 0.3625 - val_acc: 0.8701 +Epoch 8/20 +375/375 [==============================] - 1s 2ms/step - loss: 0.2972 - acc: 0.8907 - val_loss: 0.3300 - val_acc: 0.8813 +Epoch 9/20 +375/375 [==============================] - 1s 2ms/step - loss: 0.2896 - acc: 0.8948 - val_loss: 0.3335 - val_acc: 0.8792 +Epoch 10/20 +375/375 [==============================] - 1s 2ms/step - loss: 0.2773 - acc: 0.8988 - val_loss: 0.3262 - val_acc: 0.8821 +Epoch 11/20 +375/375 [==============================] - 1s 2ms/step - loss: 0.2702 - acc: 0.9020 - val_loss: 0.3237 - val_acc: 0.8848 +Epoch 12/20 +375/375 [==============================] - 1s 1ms/step - loss: 0.2620 - acc: 0.9047 - val_loss: 0.3225 - val_acc: 0.8835 +Epoch 13/20 +375/375 [==============================] - 1s 2ms/step - loss: 0.2531 - acc: 0.9072 - val_loss: 0.3294 - val_acc: 0.8831 +Epoch 14/20 +375/375 [==============================] - 1s 1ms/step - loss: 0.2489 - acc: 0.9087 - val_loss: 0.3149 - val_acc: 0.8861 +Epoch 15/20 +375/375 [==============================] - 1s 2ms/step - loss: 0.2437 - acc: 0.9115 - val_loss: 0.3265 - val_acc: 0.8832 +Epoch 16/20 +375/375 [==============================] - 1s 2ms/step - loss: 0.2362 - acc: 0.9131 - val_loss: 0.3123 - val_acc: 0.8893 +Epoch 17/20 +375/375 [==============================] - 1s 2ms/step - loss: 0.2324 - acc: 0.9145 - val_loss: 0.3193 - val_acc: 0.8874 +Epoch 18/20 +375/375 [==============================] - 1s 2ms/step - loss: 0.2246 - acc: 0.9180 - val_loss: 0.3212 - val_acc: 0.8847 +Epoch 19/20 +375/375 [==============================] - 1s 2ms/step - loss: 0.2232 - acc: 0.9179 - val_loss: 0.3140 - val_acc: 0.8867 +Epoch 20/20 +375/375 [==============================] - 1s 2ms/step - loss: 0.2141 - acc: 0.9224 - val_loss: 0.3102 - val_acc: 0.8913 +</pre> + +```python +model.evaluate(X_test, y_test) +``` + +<pre> +313/313 [==============================] - 0s 765us/step - loss: 0.3447 - acc: 0.8824 +</pre> +<pre> +[0.34466418623924255, 0.8823999762535095] +</pre> +#### 옵티마이저 + ++ 학습률 : 경사하강법을 비유하자면, 산을 내려가면서 최적값을 찾아갈때 이동하는 거리. + +학습률을 너무 높게 잡는다면 최적값을 지나칠 수도 있기 때문에 적절한 학습률 조정이 필요합니다. + + + +기본 경사 하강법 옵티마이저 + ++ SGD + + + 기본 학습률은 0.01입니다. + + model.compile(optimizer='sgd') + + + 학습률 조정도 가능합니다. (0.1로 변경) + + sgd = tensorflow.keras.optimizers.SGD(learning_rate=0.1) + ++ 모멘텀 + + + SGD에서 momentum > 0 + + sgd = tensorflow.keras.optimizers.SGD(momentum=0.9) + ++ 네스테로프 모멘텀 + + + SGD의 설정에서 nesterov = True를 주어 사용합니다. + + + +<br/> + + + +경사하강법에서 학습률에 따라 최적값을 찾아갈때, 최적값과 멀리 있을 땐 높은 이동 거리로 빠르게 접근하고, + +최적값과 가까워질 땐 좁은 거리로 이동하며 최대한 최적값에 수렴해가는게 좋습니다. + +이렇게 변화할 수 있는 학습률을 가지고 있는 것이 적응적 학습률 옵티마이저입니다. + + + +<br/> + + + +적응적 학습률 옵티마이저 + ++ RMSProp + ++ Adam + ++ Adagrad + + +#### 정확도와 손실 시각화해보기 + +model.fit()의 history는 훈련 과정 중 epoch별로 loss와 acc의 결과를 담고 있습니다. + +이를 활용해 모델의 정확도와 손실을 시각화 해볼 수 있습니다. + + + +<br/> + + + +시각화를 한 결과, epoch가 증가할 수록 훈련 데이터와 검증 데이터의 loss가 같이 줄어들고, + +정확도는 동시에 증가하는 것을 볼 수 있습니다. + +즉, 과적합이 많이 나타나지 않는 괜찮은 성능의 모델이라 확인할 수 있습니다. + + + +```python +loss = history.history['loss'] +val_loss = history.history['val_loss'] +acc = history.history['acc'] +val_acc = history.history['val_acc'] + +epochs = range(1, len(loss) + 1) +fig = plt.figure(figsize=(10, 5)) + +ax1 = fig.add_subplot(1, 2, 1) +ax1.plot(epochs, loss, color='blue', label='train_loss') +ax1.plot(epochs, val_loss, color='orange', label='val_loss') +ax1.set_title('train and val loss') +ax1.set_xlabel('epochs') +ax1.set_ylabel('loss') +ax1.legend() + +ax2 = fig.add_subplot(1, 2, 2) +ax2.plot(epochs, acc, color='green', label='train_acc') +ax2.plot(epochs, val_acc, color='red', label='val_acc') +ax2.set_title('train and val acc') +ax2.set_xlabel('epochs') +ax2.set_ylabel('acc') +ax2.legend() +``` + +<pre> +<matplotlib.legend.Legend at 0x7f8940a50910> +</pre> +<img src="data:image/png;base64,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"/> + +#### 드롭아웃 + ++ 가장 많이 사용하는 과적합을 피하는 방법 중 하나입니다. + ++ 모델을 훈련할 때, 0 ~ 1사이의 확률로 랜덤적으로 뉴런의 계산을 끄고 학습을 진행합니다. + ++ 제외하지 않은 나머지 뉴런에서만 훈련이 이루어집니다. + ++ 특정 뉴런의 의존하게 되는 계산을 막을 수 있습니다. + ++ 모델을 평가할때는 모든 뉴런을 사용합니다. + + + +```python +from tensorflow.keras.layers import Dropout + +model = Sequential() + +model.add(Flatten(input_shape=(28, 28))) +model.add(Dense(100, activation='relu')) +model.add(Dropout(0.3)) # 0.3비율로 Dropout +model.add(Dense(10, activation='softmax')) + +model.compile(loss='sparse_categorical_crossentropy', + optimizer='adam', + metrics=['acc']) + +model.summary() +``` + +<pre> +Model: "sequential_9" +_________________________________________________________________ + Layer (type) Output Shape Param # +================================================================= + flatten_7 (Flatten) (None, 784) 0 + + dense_17 (Dense) (None, 100) 78500 + + dropout_4 (Dropout) (None, 100) 0 + + dense_18 (Dense) (None, 10) 1010 + +================================================================= +Total params: 79,510 +Trainable params: 79,510 +Non-trainable params: 0 +_________________________________________________________________ +</pre> + +```python +history = model.fit(X_train, y_train, + epochs=20, + validation_split=0.2) +``` + +<pre> +Epoch 1/20 +1500/1500 [==============================] - 2s 1ms/step - loss: 0.5865 - acc: 0.7941 - val_loss: 0.4211 - val_acc: 0.8484 +Epoch 2/20 +1500/1500 [==============================] - 2s 1ms/step - loss: 0.4389 - acc: 0.8434 - val_loss: 0.4098 - val_acc: 0.8489 +Epoch 3/20 +1500/1500 [==============================] - 2s 1ms/step - loss: 0.4026 - acc: 0.8535 - val_loss: 0.3723 - val_acc: 0.8635 +Epoch 4/20 +1500/1500 [==============================] - 2s 1ms/step - loss: 0.3810 - acc: 0.8605 - val_loss: 0.3629 - val_acc: 0.8709 +Epoch 5/20 +1500/1500 [==============================] - 2s 1ms/step - loss: 0.3659 - acc: 0.8661 - val_loss: 0.3551 - val_acc: 0.8708 +Epoch 6/20 +1500/1500 [==============================] - 2s 1ms/step - loss: 0.3514 - acc: 0.8716 - val_loss: 0.3622 - val_acc: 0.8639 +Epoch 7/20 +1500/1500 [==============================] - 2s 1ms/step - loss: 0.3432 - acc: 0.8738 - val_loss: 0.3498 - val_acc: 0.8747 +Epoch 8/20 +1500/1500 [==============================] - 2s 1ms/step - loss: 0.3318 - acc: 0.8772 - val_loss: 0.3304 - val_acc: 0.8803 +Epoch 9/20 +1500/1500 [==============================] - 2s 1ms/step - loss: 0.3288 - acc: 0.8775 - val_loss: 0.3341 - val_acc: 0.8805 +Epoch 10/20 +1500/1500 [==============================] - 2s 1ms/step - loss: 0.3150 - acc: 0.8828 - val_loss: 0.3430 - val_acc: 0.8723 +Epoch 11/20 +1500/1500 [==============================] - 2s 1ms/step - loss: 0.3119 - acc: 0.8841 - val_loss: 0.3254 - val_acc: 0.8847 +Epoch 12/20 +1500/1500 [==============================] - 2s 1ms/step - loss: 0.3086 - acc: 0.8851 - val_loss: 0.3414 - val_acc: 0.8780 +Epoch 13/20 +1500/1500 [==============================] - 2s 1ms/step - loss: 0.3001 - acc: 0.8880 - val_loss: 0.3325 - val_acc: 0.8831 +Epoch 14/20 +1500/1500 [==============================] - 2s 1ms/step - loss: 0.2963 - acc: 0.8879 - val_loss: 0.3212 - val_acc: 0.8842 +Epoch 15/20 +1500/1500 [==============================] - 2s 1ms/step - loss: 0.2917 - acc: 0.8915 - val_loss: 0.3254 - val_acc: 0.8861 +Epoch 16/20 +1500/1500 [==============================] - 2s 1ms/step - loss: 0.2840 - acc: 0.8941 - val_loss: 0.3248 - val_acc: 0.8859 +Epoch 17/20 +1500/1500 [==============================] - 2s 1ms/step - loss: 0.2847 - acc: 0.8938 - val_loss: 0.3197 - val_acc: 0.8875 +Epoch 18/20 +1500/1500 [==============================] - 2s 1ms/step - loss: 0.2788 - acc: 0.8957 - val_loss: 0.3405 - val_acc: 0.8804 +Epoch 19/20 +1500/1500 [==============================] - 2s 1ms/step - loss: 0.2766 - acc: 0.8964 - val_loss: 0.3131 - val_acc: 0.8906 +Epoch 20/20 +1500/1500 [==============================] - 2s 1ms/step - loss: 0.2723 - acc: 0.8971 - val_loss: 0.3260 - val_acc: 0.8868 +</pre> +시각화를 해보면, Dropout을 사용했을 때 train_loss와 val_loss의 폭이 좀 더 줄어들고, + +val_loss의 증가율이 좀 더 낮아졌음을 볼 수 있습니다. + + + +```python +loss = history.history['loss'] +val_loss = history.history['val_loss'] +acc = history.history['acc'] +val_acc = history.history['val_acc'] + +epochs = range(1, len(loss) + 1) +fig = plt.figure(figsize=(10, 5)) + +ax1 = fig.add_subplot(1, 2, 1) +ax1.plot(epochs, loss, color='blue', label='train_loss') +ax1.plot(epochs, val_loss, color='orange', label='val_loss') +ax1.set_title('train and val loss') +ax1.set_xlabel('epochs') +ax1.set_ylabel('loss') +ax1.legend() + +ax2 = fig.add_subplot(1, 2, 2) +ax2.plot(epochs, acc, color='green', label='train_acc') +ax2.plot(epochs, val_acc, color='red', label='val_acc') +ax2.set_title('train and val acc') +ax2.set_xlabel('epochs') +ax2.set_ylabel('acc') +ax2.legend() +``` + +<pre> +<matplotlib.legend.Legend at 0x7f88e63ab9d0> +</pre> +<img src="data:image/png;base64,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"/> + +#### 모델 저장 및 복원 + ++ model.save_weights() : 모델의 구조는 저장하지않고, 파라미터만 저장합니다. (가중치, 편향) + ++ model.load_weights() : 저장된 모델 객체를 불러옵니다. + + + +<br/> + + + ++ model.save() : 모델 구조 자체를 모두 저장합니다. + ++ model.load_model() : 저장된 모델을 불러옵니다. + + + +```python +# 모델 저장 +model.save('./fashion_mnist_model-whole.h5') +``` + +model.predict() : 10개의 클래스가 존재하므로 각 샘플마다 10개의 확률을 출력해줍니다. + +10개의 확률 중 np.argmax()를 통해 가장 높은 값을 찾아서 그 인덱스를 예측값으로 사용합니다. + +얻은 예측값과 실제 테스트 데이터를 비교하여 평균을 내어 정확도를 내줍니다. + +아래에서 예측을 수행한 결과 약 88%의 정확도를 확인할 수 있습니다. + + + +```python +# model.predict() : 각 샘플마다 10개의 확률을 출력해주는 메서드 +# 10개의 확률 중 가장 높은 값을 찾아서 그 인덱스를 예측 값으로 사용합니다. + +predictions = model.predict(X_test) +val_labels = np.argmax(predictions, axis=1) # axis=1 : 행 기준으로 연산 수행(<-> axis=0 : 열 기준) +print(np.mean(val_labels == y_test)) # True : 1, False : 0 +``` + +<pre> +0.8728 +</pre> +#### 조기종료 + +학습이 진행될수록 학습셋의 정확도는 올라가지만 과적합으로 인해 테스트셋의 실험 결과가 점점 나빠질 수 있습니다. + +이렇게 학습이 진행되어도 텟트셋 오차가 줄지 않을 경우 학습을 멈추게 하는 함수입니다. + + + +<br/> + + + ++ patience = 2 : 검증셋의 손실이 2번 증가하면 중지 + ++ restore_best_weights=True : 가장 손실이 낮았던 곳으로 되돌리기 + + + +```python +import keras + +checkpoint_cb = keras.callbacks.ModelCheckpoint('fashion_mnist_model-whole.h5') +early_stopping_cb = keras.callbacks.EarlyStopping(patience=2, + restore_best_weights=True) + +history = model.fit(X_train, y_train, epochs=50, verbose=0, + validation_split=0.2, callbacks=[checkpoint_cb, early_stopping_cb]) + +print(f"종료될 떄의 epoch : {early_stopping_cb.stopped_epoch}") + +loss = history.history['loss'] +val_loss = history.history['val_loss'] + +plt.plot(loss) +plt.plot(val_loss) +plt.xlabel('epoch') +plt.ylabel('loss') +plt.legend(['train', 'val']) +plt.show() +``` + +<pre> +종료될 떄의 epoch : 11 +</pre> +<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAYgAAAEGCAYAAAB/+QKOAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8QVMy6AAAACXBIWXMAAAsTAAALEwEAmpwYAAAuQElEQVR4nO3de3zU9Z3v8dcn9yvkToAEgoS7CkJEJHTFegFbV+1KW6q19rYu29qtnp6zdc/Z7ba7p2ft6W6PvWjVWm27a2ut1mpbC9ZWVECUBFHuEpBLCCEXSEgggVy+54/fhIQwgUmYyS+ZvJ+PRx4z85vfb+YzXuY93+/v9/1+zTmHiIhIbzF+FyAiIkOTAkJERIJSQIiISFAKCBERCUoBISIiQcX5XUA45eTkuKKiIr/LEBEZNsrLy+ucc7nBnouqgCgqKqKsrMzvMkREhg0z29fXc+piEhGRoBQQIiISVEQDwsyWmtlOM6sws/v62GexmW0ys61m9mp/jhURkciJ2DkIM4sFHgSuAyqBDWb2gnNuW499MoCHgKXOuf1mlhfqsSIi4dDW1kZlZSWtra1+lxJRSUlJFBQUEB8fH/IxkTxJPR+ocM7tATCzp4CbgZ5f8rcBv3bO7QdwztX041gRkQtWWVlJeno6RUVFmJnf5USEc476+noqKyuZNGlSyMdFsotpPHCgx+PKwLaepgKZZrbazMrN7FP9OBYAM7vLzMrMrKy2tjZMpYvISNHa2kp2dnbUhgOAmZGdnd3vVlIkWxDB/mn3njo2DpgHXAMkA2+Y2foQj/U2Ovco8ChASUmJpqYVkX6L5nDoMpDPGMkWRCVQ2ONxAVAVZJ+Vzrnjzrk64DVgdojHhsXJ9g4eeXU3a3bVReLlRUSGrUgGxAZgiplNMrMEYDnwQq99ngc+YGZxZpYCXAFsD/HYsIiPieHR1/bw7MbKSLy8iMg5NTQ08NBDD/X7uA996EM0NDSEv6AeIhYQzrl24G5gFd6X/tPOua1mtsLMVgT22Q6sBN4F3gIec85t6evYSNQZE2MsLM5hbUUdWjxJRAZbXwHR0dFxzuNefPFFMjIyIlSVJ6JTbTjnXgRe7LXt4V6Pvw18O5RjI6V0cja/faeK3bXNFOelD8ZbiogAcN9997F7927mzJlDfHw8aWlpjB07lk2bNrFt2zZuueUWDhw4QGtrK1/+8pe56667gO6phZqbm7nhhhtYtGgR69atY/z48Tz//PMkJydfcG1RNRfTQJUW5wCwZledAkJkBPvGb7eyrepYWF9z5rhR/PNfzurz+fvvv58tW7awadMmVq9ezYc//GG2bNly+nLUxx9/nKysLFpaWrj88su59dZbyc7OPuM1du3axS9+8Qt+9KMf8bGPfYxnn32WT37ykxdcu6baAAqzUpiQlcLa3fV+lyIiI9z8+fPPGKvwve99j9mzZ7NgwQIOHDjArl27zjpm0qRJzJkzB4B58+axd+/esNSiFkRAaXE2v3v3EO0dncTFKjdFRqJz/dIfLKmpqafvr169mpdffpk33niDlJQUFi9eHHQsQ2Ji4un7sbGxtLS0hKUWfRMGLJycQ1NrO5sPNvpdioiMIOnp6TQ1NQV9rrGxkczMTFJSUtixYwfr168f1NrUgghYONnr01u3u57LJmT6XI2IjBTZ2dmUlpZy8cUXk5yczJgxY04/t3TpUh5++GEuvfRSpk2bxoIFCwa1NoumSztLSkrchSwYdMN3XyczJZ6f//Xg/ksQEf9s376dGTNm+F3GoAj2Wc2s3DlXEmx/dTH1UDo5m7J9R2ltO/f1xyIiI4ECoofSKTmcau+kbO9Rv0sREfGdAqKH+UVZxMUYa3drXiYREQVED6mJcVw2IYO1FQoIEREFRC+lxTlsPthI44k2v0sREfGVAqKX0uIcnIM39mhUtYiMbAqIXmYXZJCSEKtuJhEZktLS0gbtvRQQvSTExXDFpCydqBaREU8BEURpcQ57ao9zqDE885mIiPTlq1/96hnrQXz961/nG9/4Btdccw1z587lkksu4fnnn/elNk21EcTCyd7032sr6lk2r8DnakRk0PzhPqjeHN7XzL8Ebri/z6eXL1/OPffcwxe+8AUAnn76aVauXMm9997LqFGjqKurY8GCBdx0002Dvna2AiKI6fnpZKcmsK6iTgEhIhF12WWXUVNTQ1VVFbW1tWRmZjJ27FjuvfdeXnvtNWJiYjh48CCHDx8mPz9/UGtTQAQRE2NcOTmbNYFlSAc7tUXEJ+f4pR9Jy5Yt45lnnqG6uprly5fz5JNPUltbS3l5OfHx8RQVFQWd5jvSdA6iD6XFOdQ0nWR3bbPfpYhIlFu+fDlPPfUUzzzzDMuWLaOxsZG8vDzi4+N55ZVX2Ldvny91KSD6sKi4+zyEiEgkzZo1i6amJsaPH8/YsWO5/fbbKSsro6SkhCeffJLp06f7Upe6mPpQmJVCYVYyayrquHNhkd/liEiU27y5++R4Tk4Ob7zxRtD9mpsHr1dDLYhzKJ2cw/o99bR3dPpdiojIoFNAnENpsbcM6ZaqY36XIiIy6BQQ59C1DKmm3RCJbtG0smZfBvIZFRDnkJ2WyPT8dAWESBRLSkqivr4+qkPCOUd9fT1JSUn9Oi6iJ6nNbCnwXSAWeMw5d3+v5xcDzwPvBzb92jn3L4Hn9gJNQAfQ3teaqZG2qDiHn63fR2tbB0nxsX6UICIRVFBQQGVlJbW1tX6XElFJSUkUFPRv4G/EAsLMYoEHgeuASmCDmb3gnNvWa9fXnXM39vEyVzvnfP35Xlqcw2Nr3qds71EWTcnxsxQRiYD4+HgmTZrkdxlDUiS7mOYDFc65Pc65U8BTwM0RfL+ImD9Jy5CKyMgUyYAYDxzo8bgysK23K83sHTP7g5nN6rHdAS+ZWbmZ3dXXm5jZXWZWZmZlkWgidi1Duk7nIURkhIlkQASbwKj3WaCNwETn3Gzg+8BvejxX6pybC9wAfNHM/iLYmzjnHnXOlTjnSnJzc8NQ9tkWTs7hXS1DKiIjTCQDohIo7PG4AKjquYNz7phzrjlw/0Ug3sxyAo+rArc1wHN4XVa+WDRFy5CKyMgTyYDYAEwxs0lmlgAsB17ouYOZ5VtgqlQzmx+op97MUs0sPbA9Fbge2BLBWs+paxnSdToPISIjSMSuYnLOtZvZ3cAqvMtcH3fObTWzFYHnHwaWAX9rZu1AC7DcOefMbAzwXCA74oCfO+dWRqrW80mIi2H+pCzW6DyEiIwgER0HEeg2erHXtod73P8B8IMgx+0BZkeytv5aVJzD//79dqobW8kf3b/BJiIiw5FGUoeoexlStSJEZGRQQIRoen46WakJCggRGTEUECGKiTEWTs5m7e66qJ6zRUSkiwKiH0qLczh87CS7a4/7XYqISMQpIPqhVOchRGQEUUD0w4RsbxlSBYSIjAQKiH4qnZzDG3vq6ejUeQgRiW4KiH5aGFiGdPPBRr9LERGJKAVEP2kZUhEZKRQQ/ZQTWIZU8zKJSLRTQAxAaXEOG/YepbWtw+9SREQiRgExAIuKczjV3kn5vqN+lyIiEjEKiAE4vQypzkOISBRTQAxAamIccwozFBAiEtUUEANUWpzD5oONNLZoGVIRiU4KiAEqLc6h08F6LUMqIlFKATFAcwozSI6PVTeTiEQtBcQAJcTFcMVFWQoIEYlaCogLUDo5h921x6lubPW7FBGRsFNAXICFxZp2Q0SilwLiAszIH+UtQ6ppN0QkCikgLkBMjHHl5GzWVdRrGVIRiToKiAtUOjmH6mOtWoZURKKOAuICLSr2liHV7K4iEm0UEBdoQnYKBZlahlREoo8CIgxKJ+fwxm4tQyoi0SWiAWFmS81sp5lVmNl9QZ5fbGaNZrYp8Pe1UI8dSkqn5HCstZ0tWoZURKJIXKRe2MxigQeB64BKYIOZveCc29Zr19edczcO8Ngh4fQypLvrmF2Y4W8xIiJhEskWxHygwjm3xzl3CngKuHkQjh10XcuQ6jyEiESTSAbEeOBAj8eVgW29XWlm75jZH8xsVj+PxczuMrMyMyurra0NR90DUlqcQ5mWIRWRKBLJgLAg23qfxd0ITHTOzQa+D/ymH8d6G5171DlX4pwryc3NHWitF6y0OJuT7Z1s1DKkIhIlIhkQlUBhj8cFQFXPHZxzx5xzzYH7LwLxZpYTyrFDzfxJ2cTFGGvUzSQiUSKSAbEBmGJmk8wsAVgOvNBzBzPLNzML3J8fqKc+lGOHmrSuZUh3awEhEYkOEQsI51w7cDewCtgOPO2c22pmK8xsRWC3ZcAWM3sH+B6w3HmCHhupWsNlYXEOmysbtAypiEQFi6ZJ5kpKSlxZWZlv7//mnno+/uh6HrljHktm5ftWh4hIqMys3DlXEuw5jaQOo8smZJIcH8s6nYcQkSiggAijhLgY5k/K0olqEYkKCogwKy3O1jKkIhIVFBBhVqrpv0UkSiggwqxrGVJ1M4nIcKeACLOYGOPKi7QMqYgMfwqICCgt9pYh3VOnZUhFZPhSQERAaXFg+m91M4nIMKaAiIAJWVqGVESGPwVEBJiZliEVkWFPAREhC4uztQypiAxrCogIWTjZGw+xVuMhRGSYUkBESG66twzpugpN/y0iw5MCIoIWTs5hw94jWoZURIYlBUQELZqiZUhFZPhSQERQ1zKkOg8hIsORAiKC0hLjmF2YwRqdhxCRYUgBEWGlWoZURIYpBUSElU7OptN5y5GKiAwnCogI61qGVNNuiMhwo4CIsK5lSNfuVgtCRIaXkALCzL5sZqPM82Mz22hm10e6uGhRWpxNRU0zh49pGVIRGT5CbUF81jl3DLgeyAU+A9wfsaqizOlpN9TNJCLDSKgBYYHbDwFPOOfe6bFNzmPm2FFkpsSzVpe7isgwEmpAlJvZS3gBscrM0oHOyJUVXWJijIWTc1i3u07LkIrIsBFqQHwOuA+43Dl3AojH62Y6JzNbamY7zazCzO47x36Xm1mHmS3rsW2vmW02s01mVhZinUPWwuJsDjVqGVIRGT5CDYgrgZ3OuQYz+yTwj8A5Fzows1jgQeAGYCbwCTOb2cd+3wJWBXmZq51zc5xzJSHWOWQtKvbOQ6zTeQgRGSZCDYgfAifMbDbw98A+4GfnOWY+UOGc2+OcOwU8BdwcZL8vAc8CNSHWMixNyEphfEayzkOIyLARakC0O6/z/Gbgu8657wLp5zlmPHCgx+PKwLbTzGw88BHg4SDHO+AlMys3s7v6ehMzu8vMysysrLa2NoSP4g8zo7Q4m3W767QMqYgMC6EGRJOZ/QNwB/D7QLdQ/HmOCXaVU+9vxgeArzrngi2YUOqcm4vXRfVFM/uLYG/inHvUOVfinCvJzc09T0n+Ki3O4VhrO1urtAypiAx9oQbEx4GTeOMhqvFaAt8+zzGVQGGPxwVAVa99SoCnzGwvsAx4yMxuAXDOVQVua4Dn8LqshrXu8RDqZhKRoS+kgAiEwpPAaDO7EWh1zp3vHMQGYIqZTTKzBGA58EKv153knCtyzhUBzwBfcM79xsxSA5fSYmapeAP0tvTngw1FuemJTBuTrgFzIjIshDrVxseAt4CPAh8D3ux5SWowzrl24G68q5O2A08757aa2QozW3GetxwDrDGzdwLv+3vn3MpQah3qSou1DKmIDA8WysCtwBf1dYHuHswsF3jZOTc7wvX1S0lJiSsrG9pDJl57r5ZPPf4Wt84t4Fu3XkJcrOZLFBH/mFl5X0MJQv12iukKh4D6fhwrPXxgSg73XDuFZzdW8sWfb1RLQkSGrFC/5Fea2Soz+7SZfRr4PfBi5MqKXmbGPddO5Ws3zmTV1sN87qcbOH6y3e+yRETOEupJ6v8BPApcCswGHnXOfTWShUW7zy6axL9/dDbr9xzh9sfepOHEKb9LEhE5Q1yoOzrnnsUb8SxhsmxeAelJcXzp52/z8UfW85+fm0/eqCS/yxIRAc7TgjCzJjM7FuSvycyODVaR0WzJrHye+MzlHDh6gmUPv8H++hN+lyQiApwnIJxz6c65UUH+0p1zowaryGhXWpzDk5+/gsaWNpY9vI73Djf5XZKIiK5EGioum5DJ039zJQAfe+QNNh1o8LcgERnxFBBDyLT8dJ5ZsZD0pDhu/9F6TQ0uIr5SQAwxE7JTeGbFQgoyU/j0ExtYtbXa75JEZIRSQAxBY0Yl8cu/WcDMcaP4wpMbeba80u+SRGQEUkAAvP0kHN3ndxVnyEhJ4MnPX8GCi7L4yq/e4Ym17/tdkoiMMAqIE0fgpf8FP/kwHNnjdzVnSE2M48d3Xs71M8fwjd9u44GX3yOUubNERMJBAZGSBZ96AU4dhyc+BHW7/K7oDEnxsTx0+1xunVvAAy/v4l9+t41OrUgnIoNAAQEw9lL49O+hs90LiZrtfld0hrjYGL697FI+U1rEE2v38vfPvkt7R6ffZYlIlFNAdBkzEz79IliM191Uvdnvis4QE2N87caZ3HvtVJ4p10ywIhJ5CoiecqfCZ16EuCT4yY1Q9bbfFZ3BzPjytVP457/UTLAiEnkKiN6yJ3shkTQKfnozHNjgd0Vn+UzpJP5DM8GKSIQpIILJLPK6m1Kz4T9vgX1v+F3RWW6dV8BDt89lW9UxPv7IemqOtfpdkohEGQVEXzIKvZAYNQ7+66/g/df8rugsS2bl8xPNBCsiEaKAOJdRY72rmzKL4MmPQsXLfld0loXFOfz8rxdwrFUzwYpIeCkgzictD+78HeRMgV98Anau9Luis8wpzOCXd3XPBPv2/qM+VyQi0UABEYrUbG8w3ZhZ8MtPwvbf+l3RWbpmgh2VFM/tj73JWs0EKyIXSAERqpQs+NTzMO4yePpO2DL0Vl/1ZoK9ksLMFD6jmWBF5AIpIPojaTTc8WuYsACe/Ty885TfFZ0lTzPBikiYKCD6KzEdbv8VFC2C51bAxv/0u6Kz9J4J9pFXd2v+JhHpNwXEQCSkwm1PQ/E18MLdsOExvys6S2piHI9/+nJuuDiff/vDDm59eB3bDx3zuywRGUYiGhBmttTMdppZhZndd479LjezDjNb1t9jfROfDMt/DlNvgN9/Bdb/0O+KzpIY580E+x8fnc2++hPc+P01/J8Xt2t6DhEJScQCwsxigQeBG4CZwCfMbGYf+30LWNXfY30Xlwgf+xnMuAlW3gdrHvC7orOYGbfOK+DPX7mKj84r4NHX9nDdd17lj9sO+12aiAxxkWxBzAcqnHN7nHOngKeAm4Ps9yXgWaBmAMf6Ly4Blj0BFy+Dl/8ZXv2/flcUVEZKAvffeinPrLiS9KR4/vpnZfz1z8o42NDid2kiMkRFMiDGAwd6PK4MbDvNzMYDHwEe7u+xPV7jLjMrM7Oy2traCy56QGLj4K8ehdm3wSvfhD/9KwzRld9KirL43d8t4r4bpvP6rlqu+86r/Oi1PVpfQkTOEsmAsCDben9rPgB81TnXe2GDUI71Njr3qHOuxDlXkpub2/8qwyUmFm5+EObeCa//O/zxn4ZsSMTHxrDiqsn88d6ruPKibL754nb+8gdr2agR2CLSQ1wEX7sSKOzxuACo6rVPCfCUmQHkAB8ys/YQjx16YmLgxgcgNgHWfR862mDp/WDB8s5/hVkpPHZnCau2HubrL2zl1h+u47b5E/j7JdMZnRLvd3ki4rNIBsQGYIqZTQIOAsuB23ru4Jyb1HXfzH4C/M459xszizvfsUNWTAx86NveCew3fgDtJ+HD3/G2D0FmxtKL81k0JYf/98f3eGLt+6zaWs0/3TiTm2aPw4ZouIlI5EXsW8s51w7cjXd10nbgaefcVjNbYWYrBnJspGoNOzO4/n/Dov8G5U/AC1+CzqG9PGhaYhz/dONMXrh7EeMzkvnyU5u448dv8X7dcb9LExGfmBui/eQDUVJS4srKyvwuo5tz8Oq3YPW/wSUfg1t+6J3QHuI6Oh0/f2s//3flDk62d/KFxZP528WTSYyL9bs0EQkzMyt3zpUEe25o9ntECzNYfB9c8zXY/DQ8+znvvMQQFxtj3LFgIn/6ylUsmZXPAy/v4oYHXmedZogVGVEUEIPhA1+B678J237jzQTbftLvikKSl57E9z9xGT/77Hw6nOO2x97k3l9uoq55eNQvIhdGXUyD6a0fwYv/HS5a7E3REZ/c6y8l+G1csu8nuVvbOnjolQp++OpukuNjue+GGSy/vJCYGJ3EFhnOztXFpIAYbOU/gRf/B3Sc6t9xcUm9wiNwPy4pSLD0Cpnsi+CiD4YlZCpqmvnH32xm/Z4jzJ2QwTc/cgkzxo664NcVEX8oIIaa9pNw6ji0tQT+TgS/bW/t+7k+twX+2ntNoZE7Az7w32DWX13wiXLnHM+9fZBv/n47DS1tfG7RJO65dgopCUP/BLyInEkBMRJ1dnYHTMWfYM3/g9rtkDERSv8O5nwS4pMu6C0aTpziWyt38Iu3DjA+I5mv3zSL62aOCdMHEJHBoIAQLzDe+wO8/h04WAZpY2DBF6Dks5B0YV1EZXuP8L+e28LOw01cP3MM91w7lRlj0zXITmQYUEBIN+dg7+vw+n/AntXeMqrz74Ir/hZSswf8sm0dnfx4zfs88PJ7tLZ1UpSdwpKL81k6K5/ZBRk6mS0yRCkgJLiD5V7X0/bfeiez594JC++G0QUDfsn65pO8tO0wf9hSzbqKOto7HfmjklgyawxLLs5nflEWcbG6ulpkqFBAyLnV7vQWO9r8NGAw++NQei/kFF/Qyza2tPHnHYdZuaWaV9+rpbWtk6zUBK6bMYalF+ezsDhbo7NFfKaAkNA07Pdmod34M+9Kq5k3efNJjZtzwS994lQ7r+6sZeXWav68vYamk+2kJ8bxwRl5LJ2Vz1XTcnUVlIgPFBDSP821sP4h2PAYnDwGk6/xRoNPXBiWqctPtnewbnc9KzdX88fthzly/BSJcTFcNTWXGy7J54PTxzA6WdONiwwGBYQMTGsjbPixFxbHa6HwCq9FMXVJ2Na4aO/oZMPeo6zaWs3KLdVUH2slLsZYWJzD0ln5XDdzDLnpiWF5LxE5mwJCLkxbC7z9X7D2e9C4H8ZcDIvuhZm3hHV22s5OxzuVDawMhMW++hOYweVFWSydlc+Si/MZn5EctvcTEQWEhEtHG2x+xrvyqW4nZBZB6T0w5zZvgaQwcs6xo7qJlVuqWbW1mh3VTQBcWjCapYHLZy/KTQvre4qMRAoICa/OTtj5ojeWomojpOXDlV+Eks9AYnpE3nJPbTOrth5m5dZq3jnQAMDUMWksnZXP9bPymTVulAbmiQyAAkIiwzl4/1UvKN5/DZIy4Iq/gStWQEpWxN62qqGFl7ZW84ct1WzYe4ROBwWZySyZlc/Si/OZOyGTWA3MEwmJAkIir7Ic1nwHdvzOG3Q379Nw5d0wenxE37a++SQvb/fGWqytqOdURyc5aQlcNzOfJbPGsHByDglxGpgn0hcFhAyemu2BQXe/AosJ26C7UDS1tvHKzlpWba1m9Y4ajp/qOD3WYsmsfK6amktqosZaiPSkgJDBd3SfN+ju7f8M+6C7ULS2dbC2oo5VW6v547bDHD3RRmJcDB+YksuSWWO4dsYYMlMTBqUWkaFMASH+aa6B9T/sMejug4FBd6VhG0txPj3HWqzaWs2hxlZiY4wrJmWx9OJ8rp+ZT/7oC5v6XGS4UkCI/3oPuiuY7y1gNGXJoC6n6pzj3cpGb2De1mr21B4HYE5hBktmeectdPmsjCQKCBk6eg+6y5vpDboLw0p3A1FR0+RdPrulms0HGwHv8lkvLHT5rEQ/BYQMPR1tsOXZwEp3O8K60t1AHQxcPrsyyOWzS2blM2+iLp+V6KOAkKGrsxPeW+mNpThYBql5cOUXoORzF7zS3YXounx21dbDrNlVd/ry2Wumj+Hq6XksmpJDmq6IkijgW0CY2VLgu0As8Jhz7v5ez98M/CvQCbQD9zjn1gSe2ws0AR1Ae18foCcFxDB2eqW778CeVyBxNMz/vLfSXVqur6U1tbaxOjBV+Ws7a2k62U58rDF/UhZXT8vj6ul5XJSTqq4oGZZ8CQgziwXeA64DKoENwCecc9t67JMGHHfOOTO7FHjaOTc98NxeoMQ5VxfqeyogosTBjd0r3cUlwdw7YOGXIGOC35XR1tFJ2d6jrN5Zw5931LCrphmACVkpXD0tl6un57HgomyS4rUQkgwPfgXElcDXnXNLAo//AcA592/n2P9x59yMwOO9KCBGttr3YO134d2nvMeXfNSbHDBvuq9l9XTgyAlWv1fL6h01rN1dR2tbJ0nxMSycnMPV0/O4elouBZkpfpcp0ie/AmIZsNQ59/nA4zuAK5xzd/fa7yPAvwF5wIedc28Etr8PHAUc8Ihz7tHzvacCIko1VsK6H0D5T6C9Babf6A26K5jnd2VnaG3rYP2eelbvrOXPO2rYf+QEAFPy0vjg9DwWT8ujpCiTeK3JLUOIXwHxUWBJr4CY75z7Uh/7/wXwNefctYHH45xzVWaWB/wR+JJz7rUgx90F3AUwYcKEefv27YvI55Eh4HgdvPkwvPWoN65i0lVe11POVG9ywIS0QRt8dz7OOfbUHeeVHTWs3lnLm+/X09bhSE+M4wNTc1g8LY/F03LJS9cAPfHXsOhiCuzzPnB5724lM/s60Oyc+/dzvadaECNE6zEofwLeeBCaD3dvj02A5CwvLJKzICUTUrJ7bcvucT/Lm4F2EAbqNZ9sZ21FHat31vDKjlqqj7UCcMn40Vw9LZfF0/OYXZChy2hl0PkVEHF4J6mvAQ7inaS+zTm3tcc+xcDuwEnqucBvgQIgBYhxzjWZWSpeC+JfnHMrz/WeCogRpq3Vm268uQZajsCJI3CiHlqOevdbAo9PHAHX0ceLGCRnnhkaXbdn3A8Ezaix3v4XwDnH9kNNvLKzhld21LBx/1E6HWSlJnDV1FwWT8vlqqm5ZKRoriiJvHMFRMQu5HbOtZvZ3cAqvMtcH3fObTWzFYHnHwZuBT5lZm1AC/DxQFiMAZ4LXDYYB/z8fOEgI1B8krc+9vk4580DdeJIj+DoFSBd2xoPQvVm7357S/DXy5gAY2d7f/mB2/QxIZdtZswcN4qZ40bxxauLaThxitd21bF6Rw2r36vlubcPEmMwd0ImCy7KZk5hBnMmZJCTprW5ZXBpoJxIX06d6BUmR+DoXqh+Fw69A0f2dO+blh8IjUu7w2N0Yb/PiXR0Ot6tbOCVnbWs3lnD1qpjdHR6/48WZiUzpzDTC4zCDGaNG3Xhl9M6582NdXSfd5s3w1tKdoicy5HI00hqkUhobfRaG4cCgXHoHW+tbtfpPZ+cCfk9AmPsHMi6qF/nPFpOdbClqpG39x9l04EGNu1voKrRO38RH2vMGDuKOYUZXDYhgzmFmRRlp5w9YO9kkxcAR/dCwz7vfkPX4/3QduLM/VPzoHA+FFzu3Y67DOKTB/yPSYY2BYTIYDl1Amq2waFNgdB413vcccp7PiEN8i/p0UV1KeROg9j4kN+i5lgrbx9oOB0Y2yvrGN12mEKrZVpiPZelNzIlvp6xroa0lkpiWo6c+QIJ6ZA50WspZEzsvp+c5bWOKjfAgbfgyG5v/5g4r+aC+d3BkTFBrYwooYAQ8VP7KW9CwkPvdHdPVW/u/uUemwhjZvVoaVwKebO6Jy3s7PSu1ur69d+rJeCOHcS6Wi1AG3Ec7Mxmv8vjgMujOXk8SXmTyC6YRlHxTKYWTSAhlK6p43XdYVG5AQ6Wd9eclg+Fl3eHxtg5vk2yOGR0dnj/bmp3ei2zuERITO/+S0g783Hc0DinpIAQGWo6O6C+ItA9tam7tXHSm3KcmDjImQadbd6XTXvrmcenjz3z13/P++ljaW5zbK5sZNOBhtPdUzVNJwFIiIth1rhRp89lzJ2QSUFm8vnnkupoh5qtXmAceAsq3/K+EAFi4r1gK5jvBUfhFTC6IIz/wIaQthbv313tTqh7r/u2vqK7pRiKmPhAWKRB4qgeARK4Teh5Py1iYaOAEBkOnPNaBl3nM6q3QFxC4Mu/qDsIMgr7fU7AOcehxlavWyrQNfXuwQZa27yWR3ZqArMLM5g6Jp3ivDSm5KUxOS/t/DPWNtf0amVs7L76K31cr1bG7CHzqzkkLUe96V7qdp4ZBg378SZ4wFt3PbPIG6yZM9XrLsyZBlmTvLA42eydAzrV5N2e9TjYtq7Hzd5fKNLy4b/vHNDHVECIyFnaOzrZebiJt/d7obG5spE9dc20dXR/J4wbnUTxmHSKc9OYMiaN4rw0inPT+l7Pu6MNDm+BAxvgwJteK6Nhv/dcbIIXEl2tjLyZkJAK8SneX1zi4J/XcA6OVQVCIBAGdbu8IDhe071fbCLkTOkRAoHbrMmR7Vrr7PRCoiswTvYIlp6PY2K9hbcGQAEhIiFp7+hk35ETVNQ0n/7bVdPE7prjtLR1DzbMSUvwwiIvjSl53a2O3PTEs7uqmg57QdHVyqh6++wuM/B+jXeFRXxyIDySu7clpPTj+a7HyRCf6n2JN1V3twROdw3t8n65d0ka7bUAcqcGbgNhkDHB+xKOQgoIEbkgnZ2Ogw0tVNQ2U3G4Ozh21TTT1Np+er/0pDim9AqO4rw0xmckE9M1jUj7KTi8Ger3eCe9u/5OnfD699uOe7enArdnPR94HCxkQpU+rjsEcqZ0dw2l5Y24q7MUECISEc45aptOBgKjOzgqapqpa+4+YZscH8vkvNRAV1U6k3PTmDE2nQlZQcZthKqzMxAWPUPlRJDQCfyl5nYHgo+rFQ41vky1ISLRz8zIG5VE3qgkFhbnnPHc0eOnvBZHTTO7DjdTUdvMW+8f4Tebqk7vk5OWwNwJmZQUZTJvYiYXjx9NYlyIXTkxMYGrfNIAf1cdjFYKCBGJiMzUBC5PzeLyoqwztjefbGd3TTNbqhop33eU8n1HeWmbNytvQmwMlxSMpmRiJnMneqGhOaj8oy4mEfFdbdNJyvcdZeP+o5TtPcKWg8c41eFdgluUncK8iVnMm+i1NIpz07rPZ8gF0zkIERlWWts62HLQa2GU7TvKxn1HqT/undMYlRTntS4mZDKvyJu8MCVBnSEDpXMQIjKsJMXHUlKURUlRFn+DdzJ8b/2JQJfUEcr3HWX1zloAYmOMmWNHMS/QJTVvYibjMjS5YDioBSEiw1LjiTY2HjhK+V7vPMamAw2nx2qMG53E3ImZlEzMZN7ELGaMTSdOa4EHpRaEiESd0SnxXD0tj6un5QHQ1tHJjkNNlAVaGOX7jvK7dw8BkJIQS3FeGhOyUpiYncLErFTvNjuVvPREndPog1oQIhK1qhpaTp/D2F3bzL76ExxsaDm9CBNAYlzM6eCYEAiOCdkpTMxKoSAzhYS46G55qAUhIiPSuIxkbspI5qbZ405va+vopKqhhX31J9h35AT764+zr/4E+4+cYG1F/RlTisQYjB2dHGhteC2OiVmBAMlOPf9khsNcdH86EZFe4mNjvC/67NSznnPOUdt80guP+kB4HPHur9p6mCPHz5zOOzs14XRrY0IgPLpaILlpQealGmYUECIiAWZGXnoSeelJZw3wA2hqbTvd2vBuvdbHhr1Hef6dKnr22CfExTBudBLjMpJP/43PSArceo8veE3xCFNAiIiEKD0pnovHj+bi8aPPeu5keweVR1vYHwiQqoYWDja0UNXQwppddRxuaqX3Kd/s1IRAeJwZHF3bclL9PYGugBARCYPEuFgm56YxOTct6POn2js5fKyVqoYWqhpbOHi0hYMN3uM9tcd5fVcdJ051nHFMQmwMYzOSGDe6uwUyPrNHiIxOJjkhcq0QBYSIyCBIiIuhMCuFwqyUoM875zjW0n661VHV2NUC8UJkbUXwVkhWagKTc1P51YqFYa9ZASEiMgSYGaNT4hmdEs/MccGnI2/r6KS6sbsVUtXQysGGFjo7IzNcQQEhIjJMxMeeuxUSbtE9AkRERAYsogFhZkvNbKeZVZjZfUGev9nM3jWzTWZWZmaLQj1WREQiK2IBYWaxwIPADcBM4BNmNrPXbn8CZjvn5gCfBR7rx7EiIhJBkWxBzAcqnHN7nHOngKeAm3vu4Jxrdt2TQaUCLtRjRUQksiIZEOOBAz0eVwa2ncHMPmJmO4Df47UiQj42cPxdge6pstra2rAULiIikQ2IYMP/zroWyzn3nHNuOnAL8K/9OTZw/KPOuRLnXElurhYuFxEJl0gGRCVQ2ONxAVDV187OudeAyWaW099jRUQk/CIZEBuAKWY2ycwSgOXACz13MLNiC0x3aGZzgQSgPpRjRUQksiI2UM45125mdwOrgFjgcefcVjNbEXj+YeBW4FNm1ga0AB8PnLQOeuz53rO8vLzOzPYNsOQcoG6Axw51+mzDVzR/Pn22oWFiX09E1YpyF8LMyvpaVWm402cbvqL58+mzDX0aSS0iIkEpIEREJCgFRLdH/S4ggvTZhq9o/nz6bEOczkGIiEhQakGIiEhQCggREQlqxAdENE8rbmaFZvaKmW03s61m9mW/awo3M4s1s7fN7Hd+1xJOZpZhZs+Y2Y7Av78r/a4pnMzs3sB/k1vM7BdmluR3TQNlZo+bWY2ZbemxLcvM/mhmuwK3mX7WOFAjOiBGwLTi7cBXnHMzgAXAF6Ps8wF8GdjudxER8F1gZWCestlE0Wc0s/HA3wElzrmL8QbDLve3qgvyE2Bpr233AX9yzk3BW9ZgWP74HNEBQZRPK+6cO+Sc2xi434T3JRN0VtzhyMwKgA8TWEckWpjZKOAvgB8DOOdOOecafC0q/OKAZDOLA1IYxnOtBeaRO9Jr883ATwP3f4o3GemwM9IDIuRpxYc7MysCLgPe9LmUcHoA+Hug0+c6wu0ioBZ4ItB99piZpfpdVLg45w4C/w7sBw4Bjc65l/ytKuzGOOcOgfdDDcjzuZ4BGekBEfK04sOZmaUBzwL3OOeO+V1POJjZjUCNc67c71oiIA6YC/zQOXcZcJxh2kURTKA//mZgEjAOSDWzT/pblQQz0gMi6qcVN7N4vHB40jn3a7/rCaNS4CYz24vXNfhBM/svf0sKm0qg0jnX1dp7Bi8wosW1wPvOuVrnXBvwa2ChzzWF22EzGwsQuK3xuZ4BGekBEdXTigemUv8xsN059x2/6wkn59w/OOcKnHNFeP/e/uyci4pfoc65auCAmU0LbLoG2OZjSeG2H1hgZimB/0avIYpOwge8ANwZuH8n8LyPtQxYxKb7Hg76mpLc57LCqRS4A9hsZpsC2/6nc+5F/0qSEH0JeDLww2UP8Bmf6wkb59ybZvYMsBHvSru3GcZTU5jZL4DFQI6ZVQL/DNwPPG1mn8MLxI/6V+HAaaoNEREJaqR3MYmISB8UECIiEpQCQkREglJAiIhIUAoIEREJSgEhMgSY2eJom5FWhj8FhIiIBKWAEOkHM/ukmb1lZpvM7JHAehTNZvYfZrbRzP5kZrmBfeeY2Xoze9fMnutaE8DMis3sZTN7J3DM5MDLp/VYA+LJwChjEd8oIERCZGYzgI8Dpc65OUAHcDuQCmx0zs0FXsUbSQvwM+CrzrlLgc09tj8JPOicm403B9GhwPbLgHvw1ia5CG8kvIhvRvRUGyL9dA0wD9gQ+HGfjDcJWyfwy8A+/wX82sxGAxnOuVcD238K/MrM0oHxzrnnAJxzrQCB13vLOVcZeLwJKALWRPxTifRBASESOgN+6pz7hzM2mv1Tr/3ONX/NubqNTva434H+/xSfqYtJJHR/ApaZWR6cXnd4It7/R8sC+9wGrHHONQJHzewDge13AK8G1uOoNLNbAq+RaGYpg/khREKlXygiIXLObTOzfwReMrMYoA34It6CPrPMrBxoxDtPAd40zw8HAqDnjKx3AI+Y2b8EXmNYzvQp0U+zuYpcIDNrds6l+V2HSLipi0lERIJSC0JERIJSC0JERIJSQIiISFAKCBERCUoBISIiQSkgREQkqP8PxQ3lFIjTGrcAAAAASUVORK5CYII="/> diff --git "a/_posts/2022-02-04-\354\210\234\354\260\250 \353\215\260\354\235\264\355\204\260\354\231\200 \354\210\234\355\231\230 \354\213\240\352\262\275\353\247\235.md" "b/_posts/2022-02-04-\354\210\234\354\260\250 \353\215\260\354\235\264\355\204\260\354\231\200 \354\210\234\355\231\230 \354\213\240\352\262\275\353\247\235.md" new file mode 100644 index 000000000000..51ce65377c98 --- /dev/null +++ "b/_posts/2022-02-04-\354\210\234\354\260\250 \353\215\260\354\235\264\355\204\260\354\231\200 \354\210\234\355\231\230 \354\213\240\352\262\275\353\247\235.md" @@ -0,0 +1,382 @@ +--- +layout: single +title: "순환신경망(RNN) 정리" +categories: 딥러닝 +tag: [python, keras, 순환신경망, 순차데이터, RNN, 딥러닝] +toc: true +toc_sticky: true + +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 8px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + + .output_prompt { + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; + -webkit-overflow-scrolling: touch; + padding: 0.8rem; + margin-top: 0; + margin-bottom: 15px; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: $code-text-color; + border: solid 1px $border-color; + border-radius: 0.3rem; + word-break: normal; + white-space: pre; + } + + .dataframe tbody tr th:only-of-type { + vertical-align: middle; + } + + .dataframe tbody tr th { + vertical-align: top; + } + + .dataframe thead th { + text-align: center !important; + padding: 8px; + } + + .page__content p { + margin: 0 0 0px !important; + } + + .page__content p > strong { + font-size: 0.8rem !important; + } + + </style> +</head> + + +다음의 출처를 참고했습니다. + +<https://wikidocs.net/22886> + + +### 순차 데이터 + +순차 데이터는 순서에 의미가 있으며, 순서가 달라질 경우 의미가 손상되는 데이터입니다. + +시간적 의미가 있는 경우 Temporal Sequence라고 하며, 일정한 시간차라면 Time Series라고 합니다. + + +### 순환 신경망(RNN) + +순환 신경망은 순차 데이터와 같이 시간의 흐름에 따라 변화하는 데이터를 학습하기 위한 인공신경망입니다. + +따라서, 과거의 출력 데이터를 재귀적으로 참조합니다. + +순환되는 고리가 있는 신경망입니다. + + + +<br/> + + + + + + +위의 그림을 보면 은닉층의 노드에서 나온 결과값이 출력층 방향으로도 보내지고, + +다시 은닉층 노드의 다음 계산을 위한 입력으로도 보내지는 특징이 있습니다. + +그러므로, 결과값 O(t)는 결과값 O(t-1)에 대한 정보가 어느정도 포함(기억)되고, + +O(t+1)은 O(t)와 O(t-1)의 정보가 어느정도 포함(기억)된다고 할 수 있습니다. + +이렇게 이전의 계산에서 나온 출력값을 다시 다음 단계 계산에서 사용하는 하나의 사이클을 Time Step이라고 합니다. + + + +<br/> + + + ++ 셀 : 은닉층에서 결과를 두 뱡향(출력층과 다음 연산)으로 내보내는 노드를 메모리 셀 혹은 셀이라고 합니다. + + + +<br/> + + + +완전 연결층에서 출력되는 값은 활성화 출력, 합성곱 층에서 출력되는 값은 특성 맵이라고 하듯이, + +셀에서 출력하는 값은 은닉 상태(Hidden State)라고 합니다. + +또한, RNN에서는 입력층/출력층을 입력벡터/출력벡터 라고 합니다. + + + +<br/> + + + +##### RNN 수식 이해하기 + + + + + + + ++ ht : 현재 타임 스텝 t에서의 은닉 상태값 + ++ wx : 입력 벡터의 입력값과 곱해질 가중치 + ++ wh : 이전 타임 스텝 t-1에서의 은닉 상태값 ht-1과 곱해질 가중치 + + + +따라서 ht를 계산하는 수식은 다음과 같습니다. + ++ ht = activation_function((wh * ht-1) + (wx * xt) + b) + ++ yt = activation_function((wy * ht) + b) + + + +RNN에서도 역시 활성화 함수를 사용하는데, 보통 tanh함수를 많이 사용합니다.(relu를 사용하기도) + +(tanh도 역시 S자 모양으로 -1 ~ 1값을 가지기 때문에 시그모이드라고 불리기도 합니다.) + + + +RNN은 이렇게 각 타임 스텝마다 wx와 wh를 동일하게 사용합니다. + +이런 부분을 보았을 때, RNN은 타임 스텝에 따라서 가중치를 공유하며 모델의 파라미터를 효율적으로 사용할 수 있습니다. + + +##### 순환 신경망의 입력과 출력 + +RNN층은 (batch_size, timesteps, input_dim) 형태의 3D 텐서를 입력받습니다. + ++ batch_size는 한번에 RNN이 학습하는 데이터의 양을 의미합니다. + ++ timesteps는 입력 시퀀스의 크기입니다. input_length와 동일합니다. + ++ input_dim은 입력의 크기입니다. + + + +```python +from tensorflow.keras.models import Sequential +from tensorflow.keras.layers import SimpleRNN + +model = Sequential() +model.add(SimpleRNN(3, input_shape=(2, 10))) +# batch_size=8, timesteps=2, input_dim=10 + +model.summary() +``` + +<pre> +Model: "sequential_3" +_________________________________________________________________ + Layer (type) Output Shape Param # +================================================================= + simple_rnn_3 (SimpleRNN) (8, 3) 42 + +================================================================= +Total params: 42 +Trainable params: 42 +Non-trainable params: 0 +_________________________________________________________________ +</pre> +RNN의 출력값은 (batch_size, output_dim)형태로 출력됩니다. + +units는 RNN층의 은닉층의 갯수를 의미하고, output_dim은 은닉층의 갯수(units)가 됩니다. + +위와 같이 (2, 10)형태의 2D 텐선을 입력하면 units는 3이고, batch_size는 알 수 없으므로 (None, 3)이 출력됩니다. + + + +```python +model = Sequential() +model.add(SimpleRNN(3, batch_input_shape=(8, 2, 10))) +# RNN층의 units = 3, batch_size=8, timesteps=2, input_dim=10 + +model.summary() +``` + +<pre> +Model: "sequential_4" +_________________________________________________________________ + Layer (type) Output Shape Param # +================================================================= + simple_rnn_4 (SimpleRNN) (8, 3) 42 + +================================================================= +Total params: 42 +Trainable params: 42 +Non-trainable params: 0 +_________________________________________________________________ +</pre> +batch_size를 기재하면 입력은 3D 텐서이고, 출력값으로 (batch_size, output_dim)이 리턴됩니다. + +하지만, return_sequences 매개 변수를 True로 지정해주면 (batch_size, timesteps, output_dim)크기의 + +3D 텐서를 리턴할 수 있습니다. + + + +<br/> + + + +##### return_sequences + +셀의 각 시점(time step)의 은닉 상태값들을 모아서 전체 시퀀스를 리턴하려면 return_sequences=True를 설정하면 + +됩니다. + + + +<br/> + + + + + + + +위의 그림은 time step가 3일 때, return_sequences=True로 설정했을 때와 아닐 때의 차이를 보여줍니다. + +return_sequences=True로 설정하면 셀의 모든 시점(time step)에 대한 은닉 상태값을 출력하고, + +return_sequences=False일 경우에 셀은 하나의 마지막 시점의 은닉 상태값만 출력합니다. + + + +```python +model = Sequential() +model.add(SimpleRNN(3, batch_input_shape=(8, 2, 10), return_sequences=True)) +# RNN층의 units = 3, batch_size=8, timesteps=2, input_dim=10 + +model.summary() +``` + +<pre> +Model: "sequential_5" +_________________________________________________________________ + Layer (type) Output Shape Param # +================================================================= + simple_rnn_5 (SimpleRNN) (8, 2, 3) 42 + +================================================================= +Total params: 42 +Trainable params: 42 +Non-trainable params: 0 +_________________________________________________________________ +</pre> +return_sequences=True를 설정하면 출력값으로 (batch_size, timesteps, output_dim) 크기의 + +3D 텐서가 출력되는 것을 확인할 수 있습니다. + + +##### RNN의 활용 + +RNN은 입력과 출력의 길이가 고정되어 있지 않습니다. 그러므로, 설계에 따라 다양한 용도로 신경망을 사용할 수 있습니다. + + + + + + + +일대다 모델 + ++ 하나의 이미지 입력에 대해서 사진의 제목을 출력하는 이미지 캡셔닝(Image Captioning)작업에 사용할 수 있습니다. + +사진의 제목은 단어들의 나열이므로 시퀀스 출력입니다. + + + +다대일 모델 + ++ 입력 문서가 긍정적인지 부정적인지 판별하는 감성 분류(sentiment classification) + ++ 메일이 정상 메일인지 스팸 메일인지 판별하는 스팸 메일 분류(spam detection)등에 사용할 수 있습니다. + + + +다대다 모델 + ++ 사용자가 문장을 입력하면 대답 문장을 출력하는 챗봇 + ++ 입력 문장으로부터 번역된 문장을 출력하는 번역기 등이 있습니다. + + +#### 양방향 순환 신경망 + + + +양방향 RNN은 이전과 이후의 시점 모두를 고려해서 현재 시점의 예측을 더욱 정확하게 할 수 있도록 고안된 방법입니다. + +기본적으로 두 개의 메모리 셀을 사용합니다. + +첫번째 셀은 기존과 동일하게 앞 시점의 은닉 상태를 전달받아 현재의 은닉 상태를 계산합니다. + +두번째 셀은 뒤 시점의 은닉상태를 전달 받아 현재의 은닉 상태를 계산합니다.(입력 시퀀스를 반대 방향으로 읽습니다.) + +이처럼 앞, 뒤 2개의 시점을 이용하여 현재 시점의 은닉 상태를 출력합니다. + + + + +```python +from tensorflow.keras.layers import Bidirectional + +model = Sequential() +model.add(Bidirectional(SimpleRNN(3, return_sequences=True), input_shape=(2, 10))) + +model.summary() +``` + +<pre> +Model: "sequential_7" +_________________________________________________________________ + Layer (type) Output Shape Param # +================================================================= + bidirectional_1 (Bidirectio (None, 2, 6) 84 + nal) + +================================================================= +Total params: 84 +Trainable params: 84 +Non-trainable params: 0 +_________________________________________________________________ +</pre> \ No newline at end of file diff --git "a/_posts/2022-02-04-\355\225\251\354\204\261\352\263\261 \354\213\240\352\262\275\353\247\235 \354\213\234\352\260\201\355\231\224.md" "b/_posts/2022-02-04-\355\225\251\354\204\261\352\263\261 \354\213\240\352\262\275\353\247\235 \354\213\234\352\260\201\355\231\224.md" new file mode 100644 index 000000000000..a6c36f9796dd --- /dev/null +++ "b/_posts/2022-02-04-\355\225\251\354\204\261\352\263\261 \354\213\240\352\262\275\353\247\235 \354\213\234\352\260\201\355\231\224.md" @@ -0,0 +1,403 @@ +--- +layout: single +title: "합성곱 신경망 시각화" +categories: 딥러닝 +tag: [python, 파이썬, keras, CNN 시각화, 합성곱 시각화, Fashion MNIST, 딥러닝] +toc: true +toc_sticky: true + +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 8px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + + .output_prompt { + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; + -webkit-overflow-scrolling: touch; + padding: 0.8rem; + margin-top: 0; + margin-bottom: 15px; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: $code-text-color; + border: solid 1px $border-color; + border-radius: 0.3rem; + word-break: normal; + white-space: pre; + } + + .dataframe tbody tr th:only-of-type { + vertical-align: middle; + } + + .dataframe tbody tr th { + vertical-align: top; + } + + .dataframe thead th { + text-align: center !important; + padding: 8px; + } + + .page__content p { + margin: 0 0 0px !important; + } + + .page__content p > strong { + font-size: 0.8rem !important; + } + + </style> +</head> + + +### CNN 시각화 + + + +이전에는 Fashion MNIST 데이터셋 분류 모델을 CNN으로 구현해보았습니다. + +이번 포스팅에서는 합성곱 계층을 시각화 해보겠습니다. + + + +<br/> + + + +먼저 이전에 CNN으로 구현해서 저장해두었던 모델을 불러와서 모델의 계층을 확인해 보았습니다. + + + +```python +from tensorflow.keras.models import load_model + +model = load_model('./models/fasion_mnist_cnn_model.h5') + +# 모델의 계층 확인 +model.layers +``` + +<pre> +[<keras.layers.convolutional.Conv2D at 0x7fc642527280>, + <keras.layers.pooling.MaxPooling2D at 0x7fc6421718b0>, + <keras.layers.convolutional.Conv2D at 0x7fc641dd48b0>, + <keras.layers.pooling.MaxPooling2D at 0x7fc6425958e0>, + <keras.layers.core.flatten.Flatten at 0x7fc642596d60>, + <keras.layers.core.dense.Dense at 0x7fc642598310>, + <keras.layers.core.dropout.Dropout at 0x7fc642598df0>, + <keras.layers.core.dense.Dense at 0x7fc642596490>] +</pre> + +```python +model.layers +``` + +<pre> +[<keras.layers.convolutional.Conv2D at 0x7fc642532e20>, + <keras.layers.pooling.MaxPooling2D at 0x7fc642532220>, + <keras.layers.convolutional.Conv2D at 0x7fc64252bc10>, + <keras.layers.pooling.MaxPooling2D at 0x7fc64252bb50>, + <keras.layers.core.flatten.Flatten at 0x7fc642528f10>, + <keras.layers.core.dense.Dense at 0x7fc6425285b0>, + <keras.layers.core.dense.Dense at 0x7fc642528a00>] +</pre> +첫번째 층인 Conv2D 계층의 가중치를 확인하기 위해 모델의 layers속성에 0번째 인덱스로 접근해서 conv객체에 저장하겠습니다. + +conv객체의 weights속성에는 그 계층의 필터와 가중치가 포함되어 있습니다. + +(필터의 갯수만큼 가중치의 갯수가 정해집니다.) + + + +```python +conv = model.layers[0] +print(conv.weights[0].shape, conv.weights[1].shape) +``` + +<pre> +(3, 3, 1, 32) (32,) +</pre> +conv객체의 weights\[0\]은 필터의 가중치, weights\[1\]은 절편입니다. + +크기를 출력해보면, 필터의 크기가 3x3x1(흑백 이미지이므로 채널은 1)이고, 32개의 필터가 사용되었음을 알 수 있습니다. + +weights\[1\]에서 필터의 갯수가 32이므로 절편의 갯수도 32임을 확인할 수 있습니다. + + + +<br/> + + + +##### 층의 가중치 분포 + + + +이제 층의 가중치 분포를 시각화해보겠습니다. + +conv객체의 weights[0]의 가중치를 numpy 배열로 변환하여 conv_weights 객체에 저장하고, + +reshape을 사용해서 2번째 차원이 1이 되도록 즉, 열 벡터가 되도록 쭉 펼쳐서 + +히스토그램을 통해 시각화를 해봅니다. + + + +```python +import numpy as np +import matplotlib.pyplot as plt + +conv_weights = conv.weights[0].numpy() +plt.hist(conv_weights.reshape(-1, 1)) +plt.xlabel('weight') +plt.ylabel('count') +plt.show() +``` + +<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAYUAAAEGCAYAAACKB4k+AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8QVMy6AAAACXBIWXMAAAsTAAALEwEAmpwYAAASHklEQVR4nO3de7BdZ13G8e9DQ8VSkNSkJb3gAaxodVDqmYqACBYQWiRVKVMUjFinOiNKvYwTvMEMg1MdLqICM6VcwmVgKrdGqmIJYFGgmJZQaCO0pbWWhiRcbNEZK4Wff+yV1004OWefk7PXOsn+fmbO7LXeddm/s/Y6efKutfe7U1VIkgRwn6ELkCStHYaCJKkxFCRJjaEgSWoMBUlSs27oAg7Hhg0bam5ubugyJOmIcu21136pqjYutOyIDoW5uTl27tw5dBmSdERJ8u+HWublI0lSYyhIkhpDQZLUGAqSpMZQkCQ1hoIkqZlaKCR5Q5J9ST4z1nZCkquS3NQ9rh9b9sIkNyf5bJKfnlZdkqRDm2ZP4U3AUw9q2wrsqKrTgR3dPEnOAC4AfrDb5jVJjplibZKkBUwtFKrqauArBzVvBrZ109uA88ba31FV91TVrcDNwFnTqk2StLC+P9F8UlXtAaiqPUlO7NpPAT4+tt4dXdu3SXIRcBHAQx7ykCmWKmm55rZeOcjz3nbJuYM879FordxozgJtC34lXFVdWlXzVTW/ceOCQ3dIklao71DYm2QTQPe4r2u/AzhtbL1TgTt7rk2SZl7fobAd2NJNbwGuGGu/IMl3JHkocDrwiZ5rk6SZN7V7CkneDjwB2JDkDuBFwCXA5UkuBG4HzgeoqhuSXA7cCNwL/EZVfWNatUmSFja1UKiqZx9i0dmHWP+lwEunVY8kaWlr5UazJGkNMBQkSY2hIElqDAVJUmMoSJIaQ0GS1BgKkqTGUJAkNYaCJKkxFCRJjaEgSWoMBUlSYyhIkhpDQZLUGAqSpMZQkCQ1hoIkqTEUJEmNoSBJagwFSVJjKEiSGkNBktQYCpKkxlCQJDWGgiSpMRQkSY2hIElqDAVJUmMoSJIaQ0GS1BgKkqTGUJAkNYOEQpLfTnJDks8keXuS+yU5IclVSW7qHtcPUZskzbLeQyHJKcBvAfNV9UPAMcAFwFZgR1WdDuzo5iVJPRrq8tE64DuTrAOOA+4ENgPbuuXbgPOGKU2SZlfvoVBVXwBeBtwO7AHuqqp/BE6qqj3dOnuAExfaPslFSXYm2bl///6+ypakmTDE5aP1jHoFDwVOBu6f5DmTbl9Vl1bVfFXNb9y4cVplStJMGuLy0ZOAW6tqf1V9HXg38Bhgb5JNAN3jvgFqk6SZNkQo3A48OslxSQKcDewGtgNbunW2AFcMUJskzbR1fT9hVV2T5J3AdcC9wCeBS4HjgcuTXMgoOM7vuzZJmnW9hwJAVb0IeNFBzfcw6jVIkgbiJ5olSY2hIElqBrl8JEmraW7rlYM8722XnDvI806TPQVJUmMoSJIaQ0GS1BgKkqTGUJAkNYaCJKkxFCRJjaEgSWoMBUlSYyhIkhpDQZLUGAqSpMZQkCQ1hoIkqTEUJEmNoSBJagwFSVJjKEiSGkNBktQYCpKkxlCQJDWGgiSpMRQkSY2hIElqDAVJUmMoSJIaQ0GS1BgKkqTGUJAkNYOEQpIHJXlnkn9LsjvJjyc5IclVSW7qHtcPUZskzbKhegqvAv6hqr4f+GFgN7AV2FFVpwM7unlJUo96D4UkDwQeD7weoKr+t6r+E9gMbOtW2wac13dtkjTrhugpPAzYD7wxySeTXJbk/sBJVbUHoHs8cYDaJGmmDREK64AzgddW1aOA/2YZl4qSXJRkZ5Kd+/fvn1aNkjSThgiFO4A7quqabv6djEJib5JNAN3jvoU2rqpLq2q+quY3btzYS8GSNCt6D4Wq+iLwH0ke0TWdDdwIbAe2dG1bgCv6rk2SZt26gZ73N4G3JTkW+DzwPEYBdXmSC4HbgfMHqk2SZtZEoZBkR1WdvVTbpKpqFzC/wKIV7U+StDoWDYUk9wOOAzZ0HyZLt+iBwMlTrk2S1LOlegq/BlzMKACu5f9D4W7g1dMrS5I0hEVDoapeBbwqyW9W1V/1VJMkaSAT3VOoqr9K8hhgbnybqnrzlOqStEJzW68cugQdwSa90fwW4OHALuAbXXMBhoIkHUUmfUvqPHBGVdU0i5EkDWvSD699BnjwNAuRJA1v0p7CBuDGJJ8A7jnQWFXPmEpVkqRBTBoKL55mEZKktWHSdx/907QLkSQNb9J3H32N0buNAI4F7gv8d1U9cFqFSZL6N2lP4QHj80nOA86aRkGSpOGsaOjsqnov8FOrW4okaWiTXj76ubHZ+zD63IKfWZCko8yk7z76mbHpe4HbgM2rXo0kaVCT3lN43rQLkSQNb6J7CklOTfKeJPuS7E3yriSnTrs4SVK/Jr3R/EZG36F8MnAK8LddmyTpKDJpKGysqjdW1b3dz5uAjVOsS5I0gElD4UtJnpPkmO7nOcCXp1mYJKl/k4bCrwDPAr4I7AGeCXjzWZKOMpO+JfUlwJaq+ipAkhOAlzEKC0nSUWLSnsIjDwQCQFV9BXjUdEqSJA1l0lC4T5L1B2a6nsKkvQxJ0hFi0n/YXw58NMk7GQ1v8SzgpVOrSpI0iEk/0fzmJDsZDYIX4Oeq6sapViZJ6t3El4C6EDAIJOkotqKhsyVJRydDQZLUGAqSpMZQkCQ1hoIkqTEUJEnNYKHQjbb6ySTv6+ZPSHJVkpu6x/VL7UOStLqG7Cm8ANg9Nr8V2FFVpwM7unlJUo8GCYXuqzzPBS4ba94MbOumtwHn9VyWJM28oXoKfwH8PvDNsbaTqmoPQPd44kIbJrkoyc4kO/fv3z/1QiVplvQeCkmeDuyrqmtXsn1VXVpV81U1v3Gj3wgqSatpiOGvHws8I8k5wP2AByZ5K7A3yaaq2pNkE7BvgNokaab13lOoqhdW1alVNQdcAHywqp4DbAe2dKttAa7ouzZJmnVr6XMKlwBPTnIT8ORuXpLUo0G/Pa2qPgx8uJv+MnD2kPVI0qxbSz0FSdLADAVJUmMoSJIaQ0GS1BgKkqTGUJAkNYaCJKkxFCRJjaEgSWoMBUlSYyhIkhpDQZLUGAqSpMZQkCQ1hoIkqTEUJEmNoSBJagwFSVJjKEiSGkNBktQYCpKkxlCQJDWGgiSpMRQkSY2hIElqDAVJUmMoSJIaQ0GS1BgKkqRm3dAFSEerua1XDl2CtGz2FCRJjaEgSWp6D4UkpyX5UJLdSW5I8oKu/YQkVyW5qXtc33dtkjTrhugp3Av8blX9APBo4DeSnAFsBXZU1enAjm5ektSj3kOhqvZU1XXd9NeA3cApwGZgW7faNuC8vmuTpFk36D2FJHPAo4BrgJOqag+MggM48RDbXJRkZ5Kd+/fv761WSZoFg4VCkuOBdwEXV9Xdk25XVZdW1XxVzW/cuHF6BUrSDBrkcwpJ7ssoEN5WVe/umvcm2VRVe5JsAvYNUZskTWrIz6Lcdsm5U9nvEO8+CvB6YHdVvWJs0XZgSze9Bbii79okadYN0VN4LPBc4NNJdnVtfwBcAlye5ELgduD8AWqTpJnWeyhU1T8DOcTis/usRZL0rfxEsySpMRQkSY2hIElqDAVJUmMoSJIaQ0GS1BgKkqTGUJAkNYaCJKkxFCRJjaEgSWoMBUlSYyhIkhpDQZLUGAqSpMZQkCQ1hoIkqTEUJEmNoSBJagwFSVJjKEiSGkNBktQYCpKkxlCQJDWGgiSpMRQkSY2hIElqDAVJUrNu6AKkaZrbeuXQJUhHFHsKkqRmpnsKQ/0v8rZLzh3keYf8X/NQv7Ok5bGnIElqZrqnMJRZvM49i7+zdCRacz2FJE9N8tkkNyfZOnQ9kjRL1lQoJDkGeDXwNOAM4NlJzhi2KkmaHWsqFICzgJur6vNV9b/AO4DNA9ckSTNjrd1TOAX4j7H5O4AfG18hyUXARd3sfyX57DL2vwH40mFVOD1rtba1WhdY20qs1brA2pYlfwasvK7vOdSCtRYKWaCtvmWm6lLg0hXtPNlZVfMr2Xba1mpta7UusLaVWKt1gbWtxDTqWmuXj+4AThubPxW4c6BaJGnmrLVQ+Ffg9CQPTXIscAGwfeCaJGlmrKnLR1V1b5LnA+8HjgHeUFU3rOJTrOiyU0/Wam1rtS6wtpVYq3WBta3EqteVqlp6LUnSTFhrl48kSQMyFCRJzVEXCknOT3JDkm8mOeRbtZLcluTTSXYl2TnWfkKSq5Lc1D2u77O2JKcl+VCS3d26Lxhb9uIkX+hq3pXknL7q6tZbcAiSKR+zJfed5BFjx2RXkruTXNwtm9Yxm+h3Hug8m+SY9XaeLTV0TUb+slt+fZIzJ932cE1Q2y92NV2f5KNJfnhs2YKvbY+1PSHJXWOv059Muu2iquqo+gF+AHgE8GFgfpH1bgM2LND+58DWbnor8Gd91gZsAs7sph8AfA44o5t/MfB7QxwzRjf+bwEeBhwLfGqsrmkes2Xtu6vzi8D3TPmYTVTXQOfZkvvu6zxb7LwZW+cc4O8ZfU7p0cA1k27bQ22PAdZ30087UNtir22PtT0BeN9Ktl3s56jrKVTV7qpazqecD7YZ2NZNbwPOO+yiOpPUVlV7quq6bvprwG5Gn/SemgmP2WJDkEztmK1g32cDt1TVv69iDQs53N950GPW43k2ydA1m4E318jHgQcl2TThtlOtrao+WlVf7WY/zuizU304nN/9sI7bURcKy1DAPya5NqOhMw44qar2wOgPBzhxkOqAJHPAo4Brxpqf33Vl37CalxwmsNAQJAf+EZnmMVvuvi8A3n5Q2zSO2aR1DXGeLWvfUz7PFjtvllpnkm0Px3L3fyGjHs0Bh3pt+6ztx5N8KsnfJ/nBZW67oDX1OYVJJfkA8OAFFv1hVV0x4W4eW1V3JjkRuCrJv1XV1WukNpIcD7wLuLiq7u6aXwu8hNHJ+BLg5cCv9FTXkkOQrNRitS1zP8cCzwBeONY8lWO2jLJ6P8+WuZ9VPc8WeooF2g4+bw61ztTOuSWe99tXTJ7IKBQeN9Y8ldd2GbVdx+gy6X91933eC5w+4baHdESGQlU9aRX2cWf3uC/Jexh1ua4G9ibZVFV7ui7svr5rS3JfRn+ob6uqd4/te+/YOq8D3tdjXYsNQTK1Y5ZkOft+GnDd+HGa1jGbtK4hzrNJa5vGebaASYauOdQ6x06w7eGYaFidJI8ELgOeVlVfPtC+yGvbS21jIU5V/V2S1yTZMMm2i5nJy0dJ7p/kAQemgacAn+kWbwe2dNNbgIn/d79KtQV4PbC7ql5x0LJNY7M/y//X3IfFhiCZ5jFbzr6fzUGXjqZ4zJasa8DzbJLa+jrPJhm6ZjvwS927kB4N3NVd9pr2sDdL7j/JQ4B3A8+tqs+NtS/22vZV24O715EkZzH69/zLk2y7qGncOR/yh9FJfAdwD7AXeH/XfjLwd930wxjdkf8UcAOjSygHtv9uYAdwU/d4Qs+1PY5RV+96YFf3c0637C3Ap7tl24FNfdXVzZ/D6F0qt/R4zBbc9wK1Hdf9QXzXQdtP65gtWdeA59kktfV2ni103gC/Dvx6Nx1GX651S/e884ttu5o/E9R2GfDVsWO0c6nXtsfant8996cY3QR/zGocN4e5kCQ1M3n5SJK0MENBktQYCpKkxlCQJDWGgiSpMRSkVZTksiRnLLHOm5I8c4H2uSS/ML3qpKUZCtIqqqpfraobV7j5HGAoaFCGgrSAJL+f5Le66Vcm+WA3fXaStyZ5SpKPJbkuyd90YwiR5MPpvpMiyYVJPte1vS7JX489xeMzGp//82O9hkuAn8hobPzf7vHXlRpDQVrY1cBPdNPzwPHdWEGPY/Sp2z8CnlRVZwI7gd8Z3zjJycAfM/p+gCcD33/Q/jd1+3o6ozCA0XcffKSqfqSqXrnqv5E0gSNyQDypB9cCP9qNb3MPoxEp5xkFxXbgDOBfuqFnjgU+dtD2ZwH/VFVfAUjyN8D3jS1/b1V9E7gxyUnT/EWk5TAUpAVU1deT3AY8D/goo3GAngg8HLgVuKqqnr3ILhYavnjcPctYV+qNl4+kQ7sa+L3u8SOMBiPbxWjwsccm+V6AJMcl+b6Dtv0E8JNJ1idZB/z8BM/3NUZfjSkNxlCQDu0jjK79f6xG3zHwP4yu+e8Hfhl4e5LrGYXEt9wzqKovAH/K6NvMPgDcCNy1xPNdD9yb0TdpeaNZg3CUVGlKkhxfo2/FWge8B3hDVb1n6LqkxdhTkKbnxUl2MfrylVsZfV2itKbZU5AkNfYUJEmNoSBJagwFSVJjKEiSGkNBktT8H+3EA/PvRdPRAAAAAElFTkSuQmCC"/> + + +```python +# 훈련 전 모델 +from tensorflow.keras.models import Sequential +from tensorflow.keras.layers import Conv2D, MaxPooling2D, Dense, Flatten, Dropout + +no_training_model = Sequential() +no_training_model.add(Conv2D(32, kernel_size=3, activation='relu', padding='same', input_shape=(28,28,1))) + + +no_training_conv = no_training_model.layers[0] +no_training_conv_weights = no_training_model.weights[0].numpy() + +conv_weights = no_training_conv.weights[0].numpy() +plt.hist(no_training_conv_weights.reshape(-1, 1)) +plt.xlabel('weight') +plt.ylabel('count') +plt.show() +``` + +<img src="data:image/png;base64,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"/> + +약 100개 정도의 원소가 0에 가까운 값을 가지고 있고, 약 20개 정도는 0.25 ~ 0.5의 값을 가지고 있습니다. + +마이너스 값을 가진 원소들도 확인 할 수 있는데요. + +학습하기전의 그래프 모양은 -부터 +까지 균등분포 형태를 가질것입니다. + +학습 전에는 가중치가 무작위로 초기화되어 규칙성이 없기 때문입니다. + +하지만, 학습을 마친 필터는 규칙성이 생기므로 이러한 분포를 나타냅니다. + +큰 양의 값과 큰 음의 값은 입력 이미지에서 어떠한 특징을 감지하기 위한 값들이고, + +0에 가까운 값일 수록 의미있는 값을 학습하는 가중치라고 할 수 없습니다. + + +##### 층의 가중치 시각화 + + + +필터를 (3x3)크기로 32개 만큼 시각화 했습니다. + +matplotlib의 imshow()는 배열의 값을 최솟값에 가까울수록 어둡게, 최댓값에 가까울수록 밝게 이미지로 출력해줍니다. + +이 수치가 동일한 기준을 갖기 위해 vmin, vmax으로 설정했습니다. + + + +<br/> + + + +가중치 시각화도 마찬가지로 학습하기 전은 흑백의 정도에 규칙성이 없이 균등하게 나타납니다. + +하지만, 학습을 마친 필터에는 나름의 규칙성이 있는 이미지화가 되어 다음과 같이 출력됩니다. + + + +```python +fig, axs = plt.subplots(2, 16, figsize=(15, 2)) + +for i in range(2): + for j in range(16): + axs[i, j].imshow(conv_weights[:, :, 0, i * 16 + j], vmin=-0.5, vmax=0.5) + axs[i, j].axis('off') + +plt.show() +``` + +<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA1MAAAB1CAYAAACrpbsqAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8QVMy6AAAACXBIWXMAAAsTAAALEwEAmpwYAAAJyElEQVR4nO3c+Y9V9R0G4DMyCgM4DpsjiMAgi4pRK2pF2xRRiFaNtlEMsS7R1NqkicWtURuLQW2qArbU1MTUmqrYaDXWxKXg0lqtGBewiFIVWUQW2RmWGZmZ23/AOTe+kwk1eZ4f+eb9cj73nuW+9yZTU6lUCgAAAL6e/fb1AQAAAHwTKVMAAAABZQoAACCgTAEAAASUKQAAgEBt2WLTvDvjP/U3qH9zGi12vHFwnF122/SasvWjb5gTz3TAjvwvHzYPj6PFxzdfWzrT6F/Pjg/syyFfptFi7PD1cXb+xHtLZzpl6j3xTDUdabIoem7dG2dffumm0pmOuDU/99p65+fekBPWxdlXz7i7dKaRs/Jzr6YLf0h0xnmPx9lLRi/sdKam382Kj6rnpi58N9WF12LZzPJ73pkNV8a7j3gpvz+8v2VwnH1t8l2lMx319Ix4pr69WtNo0fps/mx6b275+3TSCzftkz+tWz+zT5x98fVfls700EcT4pl+8+epabSolB5VuWrX0/jnbolnmnzosjRaHFG3Ns5ePuaN0pkmvzI9num4/mvSaPH25mFx9pVJszqdacTc/D7ee11+H2/tn1/Cy68v/5x3/E/yZ+3AaavTaLFmW0Oc/eD8GaUzNT2ad4y6Pvmzqc/T9XH2rYe++n3yyxQAAEBAmQIAAAgoUwAAAAFlCgAAIKBMAQAABJQpAACAgDIFAAAQUKYAAAACyhQAAEBAmQIAAAgoUwAAAAFlCgAAIKBMAQAABGrLFnv22htvvGfBwXl2bFucraZ2VyXObp6Qvx79B+2Is9X87Pzn4uy9C86Ks9sfOCzOFhPLl6+6/cl4679uOCHOLlk6LM5WU7+iI87ubsy/97hi2Gtxtpq6UdvjbO2LDXH29scuirOX3Nr5WmX//P7QMqo1znangX/Ps6+sHB1n29u677u69v8cFGd3Hbstzu4elV/D1Wx/M39mXnPR3+LsvMaz42w1c+ZOjbPD56+Ps+0ffxpni5nTS5c3rWmIt37nnPyaeKcYGmcvr3LarlyYP8s/GtIYZ3vXt8TZ0n3X5a9zpSb/f3uM2JmHq9g6rgufXT8ZEmePvP6/cbbYVr5c2dIz3vqn41+Ms38YfG6c7YxfpgAAAALKFAAAQECZAgAACChTAAAAAWUKAAAgoEwBAAAElCkAAICAMgUAABBQpgAAAALKFAAAQECZAgAACChTAAAAAWUKAAAgoEwBAAAEassW92zrFW/ce0clzu63p/s6XuvkHXl4Y584umvRgPz/Pbt8ec5LZ8ZbN4zcGmf73fZFnK1m1v1T4+zuIfm5N2Xi4jhbTW1LflytA/Lsv7aPjbOXV1k/qK4l3vvAP74bZzf+6FtxtkyPnfm9p71v/h4VNXm0mn+vGBlnR81ui7N7BveMs8VF5cstjflxNT5ZH2dbjum+N2rQe/lMr04ZE2fXTOq+mcZd/EGc/dPNL8XZUxZNi7PVnHdift/6qPHgONu+ofuetzVt+TnQ9Fie3TEsvxaLH3S+tOvwvfG2Bw7aGWc73u0XZ4sLypevPmNBvPWDH54SZzefPy7OVjPy6S/j7G8PnhRne5zQHGc745cpAACAgDIFAAAQUKYAAAACyhQAAEBAmQIAAAgoUwAAAAFlCgAAIKBMAQAABJQpAACAgDIFAAAQUKYAAAACyhQAAEBAmQIAAAgoUwAAAIGaSqWyr48BAADgG8cvUwAAAAFlCgAAIKBMAQAABJQpAACAgDIFAAAQUKYAAAACyhQAAEBAmQIAAAgoUwAAAAFlCgAAIKBMAQAABJQpAACAgDIFAAAQUKYAAAACyhQAAECgtmzxlAvvqcQ7/3hjHF2/+aA4++m0m2vK1ifvd2E807ZLJ6TRonl46WGVWnbb9NLw4XfNjmeaNGlxGi1W7ewfZ+dPvLd0pikn3RbPtHH8gWm0aKvL36cls8vfpxFzZ8UzVXq1p9Gi96r94+yHt1eZ6ff5PeLk8R+l0WLl3LFxduG86zqdafj9d8fzNLxfejst1XFAHK163nXlnrevLOh4onSm7yy4MZ6p58z8+dIxY0ucfWXSrNKZxj93SzxT+7MD0mix9dj83rLqqhtKZ2p69M54po6W/HrqilVX3lg60/RFF8UzPbPg22m0GLQ4v4zL7nlFURSnvXxdvPmqDfm5V/9arzi7+L5rO7+PP3BXPE/t1vy8a6vvwrV0dfm19Ksl58UzvTo9/+y68pz888OKn5efd2NnzIlnapq3Lo0Wqy4cHGc/vOOrn7d+mQIAAAgoUwAAAAFlCgAAIKBMAQAABJQpAACAgDIFAAAQUKYAAAACyhQAAEBAmQIAAAgoUwAAAAFlCgAAIKBMAQAABJQpAACAQG3ZYt9Vu+KNW/briLP9G3bG2e404O3NcfaFOx7qwv88vXS1MmxPvPP7WwbH2Z3PHxJni4nly6vPqo+33tP0ZZw995j34mw1V5z2jzj7+qXHx9nlv9g/zlbTdNS6OLu5pU+c7b0+f49L923M73nbO/rG2UFv+l7r6/hsxaA4O7q9Nc5OGLgizlZzYuPqOPv6AQPjbP2y0o8BXXLqqOVxdsmjR8fZ7WPa42w1T707Ps4e+fCWODvl8TfjbFFcV7r6wyGL4p3/ct/342zz0Jo4W6bhkOY429y3Ls7W7O2++/jDS0+Ks33H9Yyz+w/NX8tqTj/3nTh7wWVvxdnZa6bE2c54ggMAAASUKQAAgIAyBQAAEFCmAAAAAsoUAABAQJkCAAAIKFMAAAABZQoAACCgTAEAAASUKQAAgIAyBQAAEFCmAAAAAsoUAABAQJkCAAAI1JYt7hjVN964/wGb4+xnSw+Js8VZ5cttp4+Ptx5257I4+8iOI+PsNYeWrw97sEe8d93S3XF27a1tcbaafqeuj7N7Pu8fZ19ePSbOFseXL69tbYi33j00vxYrn+XnRzXNrT3jbI9HBsTZ9dPa42yZPWvz17nHgNY4u2lKTZz9f1V76JBu27umPX+9mpvq4uz8z4+Is3ceU76+8uIqN/oSlz31Qpy9b/H34mw1C1c2xdlBG/JrvK2u++55w57Jz73l0/Jn0ye7G+NsNc9/cXSc3XVI/p18a/5ylNr1Qb84e9gJa+Psxub8+VFNx6b8WdueR4vWrb3ycBX/fCL/PL54+XFxdv3JXfgd6btf/c9+mQIAAAgoUwAAAAFlCgAAIKBMAQAABJQpAACAgDIFAAAQUKYAAAACyhQAAEBAmQIAAAgoUwAAAAFlCgAAIKBMAQAABJQpAACAgDIFAAAQqKlUKvv6GAAAAL5x/DIFAAAQUKYAAAACyhQAAEBAmQIAAAgoUwAAAAFlCgAAIPA/ovj9CqPBBcEAAAAASUVORK5CYII="/> + + +```python +# 학습 전 +fig, axs = plt.subplots(2, 16, figsize=(15, 2)) + +for i in range(2): + for j in range(16): + axs[i, j].imshow(no_training_conv_weights[:, :, 0, i * 16 + j], vmin=-0.5, vmax=0.5) + axs[i, j].axis('off') + +plt.show() +``` + +<img src="data:image/png;base64,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"/> + +##### 합성곱의 특성맵 시각화 + + + +특성맵을 시각화 하기위해 함수형API를 사용하여 모델을 구축해야합니다. + +이전에는 Sequential클래스를 사용했지만, 보다 복잡한 모델(다중 입력 또는 다중 출력)을 구성하기 위해서는 + +함수형API를 사용합니다. + + + +<br/> + + + ++ 함수형 API + + 케라스에서 가장 권장하는 방법 중 하나입니다. + + 복잡한 모델을 유연하게 구성할 수 있습니다. + + 다중 입력, 출력이 가능합니다. + + + +<br/> + + + +```python + +from tensorflow.keras.models import Model + +from tensorflow.keras.layers import Conv2D, MaxPooling2D, Dense, GlobalAveragePooling2D + +from tensorflow.keras.layers import Input + +from tensorflow.keras.utils import plot_model + + + +# 함수형 API는 Input층을 통해서 입력값의 형태를 정합니다. + +inputs = Input(shape=(28, 28, 3)) + +x = Conv2D(32, kernel_size=3, activation='relu', padding='same')(inputs) + +x = Conv2D(32, kernel_size=3, activation='relu', padding='same')(x) + +x = MaxPooling2D(2)(x) + +x = GlobalAveragePooling2D()(x) + +x = Dense(10, activation='softmax')(x) + + + +# 위에서 정의된 층을 포함하고 있는 모델을 생성합니다. + +model = Model(inputs=inputs, outputs=x) + +``` + + +##### 첫 번째 특성 맵 시각화 + +첫번째 Conv층의 특성맵을 시각화하기 위해 앞서 기존에 만든 model의 중간 부분만 뽑아오겠습니다. + +model의 Input Layer를 입력으로, model의 첫번째 Conv층을 출력으로 지정해서 새로운 모델을 생성했습니다. + + + +<br/> + + + +conv_acti에 임의로 하나의 데이터를 입력해서 특성맵을 뽑아보았습니다. + + + + +```python +from tensorflow.keras.models import Model + +conv_acti = Model(model.input, model.layers[0].output) + +inputs = X_train[0:1].reshape(-1, 28, 28, 1) / 255.0 # X_train의 (1, 28, 28, 1) +feature_map = conv_acti.predict(inputs) + +fig, axs = plt.subplots(4, 8, figsize=(15, 8)) +for i in range(4): + for j in range(8): + axs[i, j].imshow(feature_map[0, :, :, i * 8 + j]) + axs[i, j].axis('off') +plt.show() +``` + +<img src="data:image/png;base64,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R0OfkxFM+xji0GodjufJ04PGPDIOGD8pBlSWlYVFVwQVN49f3piI2IvfD/J3ScyR+UHz/9nuJNZhnfi71Y/3rAi8Z+70GUUkff00zol1Trnx3E0rdszx0ixOzsZ1uv3ju74z5fS8M0VERERERBQBL6aIiIiIiIgi4MUUERERERFRBEX3mSpE5sHV7sd0U/SYUq6NvhBOU4M5vygPK3Pfthehz1SwhPlUf1fRsuYLWswJf06Uiw3tC5QTfc2GzHLOpSzvPJt9kZaC3LETur3ur9B+4K/EvtSIfem+LTuN+T/riD5/Ir/vib+nzo2YbyqGMJBDIyiFflq2aJu9Hy98PBtyU09CNCfChugwzonB58YX5tAVfm5p7VluD8rRy8/mn+/WbW/EPP4559FnKhd4Lq8C6zlslBY/m//vQe6+Q8VNSKSUqrRTU0+klLr93J4pp7n8fW/S7aZP74q6SHmdeP+1un3wtZ8q6WuX1p5pTc07U0RERERERBHwYoqIiIiIiCiCksT8iiJidm5Xd4EJxSwFbqHTLJKjyjfUmU8FHtP8klE8++6+AlOCDIGGh1+JiBavsNhiMNpX7HNEy8XD7xHxu/eU+tX3lPoFFwTemSIiIiIiIoqAF1NEREREREQRWH6RI70TERERERER8M4UERERERFRBLyYIiIiIiIiioAXU0RERERERBHwYoqIiIiIiCgCXkwRERERERFFwIspIiIiIiKiCHgxRUREREREFAEvpoiIiIiIiCLgxRQREREREVEEvJgiIiIiIiKKgBdTREREREREEfBiioiIiIiIKAJeTBEREREREUXAiykiIiIiIqIIeDFFREREREQUAS+miIiIiIiIIuDFFBERERERUQS8mCIiIiIiIoqAF1NEREREREQR8GKKiIiIiIgoAl5MERERERERRcCLKSIiIiIiogh4MUVERERERBQBL6aIiIiIiIgiiBV68pn2y/25WpDl7KfeN62ZvgbX1dzgulo8uK4WD66rxWOm64rraW5wn1o8uK4Wj7B1xTtTREREREREEfBiioiIiIiIKAJeTBEREREREUXAiykiIiIiIqIIChagWOzsVEq3vcnJeVwSmkpsXYduT6xrMJ6L/+yhOV4aosXLSiZ1279sM57Y/dg8LA2VQmzNKt3OnTw9j0tCRERBvDNFREREREQUAS+miIiIiIiIIuDF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"/> + +##### 두 번째 특성 맵 시각화 + + + +첫 번째 특성 맵을 시각화할때 사용한 input을 그대로 사용해서 두 번째 합성곱층의 특성 맵을 시각화해보았습니다. + +두 번째 합성곱 층은 아래의 결과를 보았을 때, 어떤 부분을 학습하는 지 유추하기 어렵습니다. + + + +```python +conv_acti2 = Model(model.input, model.layers[2].output) +feature_map = conv_acti2.predict(inputs) + +fig, axs = plt.subplots(8, 8, figsize=(15, 8)) +for i in range(8): + for j in range(8): + axs[i, j].imshow(feature_map[0, :, :, i * 8 + j]) + axs[i, j].axis('off') +plt.show() +``` + +<img src="data:image/png;base64,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"/> + +따라서, 낮은 층에서 저수준 특성(눈에 보이는 확실한 패턴)을 학습하고, 층이 깊어질수록 고수준 특성(추상적인 패턴)을 + +학습함을 알 수 있습니다. + diff --git "a/_posts/2022-02-04-\355\225\251\354\204\261\352\263\261 \354\213\240\352\262\275\353\247\235(CNN, Fashion MNIST \353\266\204\353\245\230).md" "b/_posts/2022-02-04-\355\225\251\354\204\261\352\263\261 \354\213\240\352\262\275\353\247\235(CNN, Fashion MNIST \353\266\204\353\245\230).md" new file mode 100644 index 000000000000..530727f33396 --- /dev/null +++ "b/_posts/2022-02-04-\355\225\251\354\204\261\352\263\261 \354\213\240\352\262\275\353\247\235(CNN, Fashion MNIST \353\266\204\353\245\230).md" @@ -0,0 +1,431 @@ +--- +layout: single +title: "합성곱 신경망(CNN, Fasion MNIST 분류)" +categories: 딥러닝 +tag: [python, 파이썬, keras, CNN, 합성곱, Fashion MNIST, 분류, 딥러닝] +toc: true +toc_sticky: true + +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 8px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + + .output_prompt { + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; + -webkit-overflow-scrolling: touch; + padding: 0.8rem; + margin-top: 0; + margin-bottom: 15px; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: $code-text-color; + border: solid 1px $border-color; + border-radius: 0.3rem; + word-break: normal; + white-space: pre; + } + + .dataframe tbody tr th:only-of-type { + vertical-align: middle; + } + + .dataframe tbody tr th { + vertical-align: top; + } + + .dataframe thead th { + text-align: center !important; + padding: 8px; + } + + .page__content p { + margin: 0 0 0px !important; + } + + .page__content p > strong { + font-size: 0.8rem !important; + } + + </style> +</head> + + +#### CNN을 활용한 Fashion MNIST 예측 + + + +<br/> + + + +이전에 다층 퍼셉트론을 이용하여 Fashion MNIST를 예측해 보았는데요. + +이번엔 이미지 데이터에 효과적인 CNN(컨볼루션 층 + 풀링 층)을 공부해 보았으니 + +CNN을 활용하여 Fashion MNIST를 예측하는 모델을 구성해보겠습니다. + + +이전과 동일하게 케라스에서 제공하는 Fashion MNIST 데이터를 불러오고, + +X데이터를 픽셀값 255로 나눠줌으로써 각 값들이 0 ~ 1사이의 값을 가질 수 있도록 만듭니다. + +또한, CNN을 공부할때, 입력되는 이미지 데이터는 (높이, 너비, 깊이) 형태인 것을 알 수 있었습니다. + +깊이는 이미지 데이터의 채널을 뜻하며, 흑백은 1, 컬러는 3이었죠? + +Fashion MNIST 데이터는 흑백 이미지이기 때문에 numpy의 reshape함수를 사용하여 깊이 차원을 추가해줍니다. + +그리고 모델 학습 시 과적합 여부를 확인하기 위해 훈련 데이터셋과 검증 데이터셋으로 나눕니다. + + + +```python +from tensorflow.keras.datasets import fashion_mnist +from sklearn.model_selection import train_test_split + +# 데이터 불러오기 +(X_train, y_train), (X_test, y_test) = fashion_mnist.load_data() + +# X데이터를 0 ~ 1 값을 가질 수 있도록 맞춰주기 +X_train = X_train / 255.0 +X_test = X_test / 255.0 + +# 이미지 데이터에 깊이 차원을 만들어주기 +X_train = X_train.reshape(-1, 28, 28, 1) +X_test = X_test.reshape(-1, 28, 28, 1) + +# 검증 데이터셋 분리 +X_train, X_val, y_train, y_val = train_test_split(X_train, y_train, + test_size=0.2, random_state=42) + +# 입력 데이터의 크기 확인 +X_train.shape, X_val.shape, X_test.shape +``` + +<pre> +((48000, 28, 28, 1), (12000, 28, 28, 1), (10000, 28, 28, 1)) +</pre> +#### 모델 구성 + + + +```python +from tensorflow.keras.models import Sequential +from tensorflow.keras.layers import Conv2D, MaxPooling2D, Dense, Flatten, Dropout + +model = Sequential() + +# 첫번째 합성곱 층 +model.add(Conv2D(32, kernel_size=3, activation='relu', padding='same', input_shape=(28,28,1))) +model.add(MaxPooling2D(2)) + +# 두번째 합성곱 층 +model.add(Conv2D(64, kernel_size=3, activation='relu', padding='same')) +model.add(MaxPooling2D(2)) + +# 완전 연결층 +model.add(Flatten()) +model.add(Dense(100, activation='relu')) +model.add(Dropout(0.4)) +model.add(Dense(10, activation='softmax')) # 10개의 클래스 중 택1, 10개의 확률을 얻기위해 softmax 사용 +``` + +첫번째 컨볼루션 층에서는 32개의 커널을 사용, 커널의 사이즈는 (3,3,1)이며, 활성화 함수는 relu를 사용하겠습니다. + +(입력 데이터의 채널이 1이므로 커널의 채널도 1이 됩니다. 만약 입력 데이터가 컬러이면 채널은 3이 되겠습니다.). + +same 패딩을 사용할 것이고, 입력 사이즈는 배치 차원없이 (28, 28, 1)이 됩니다. + +그럼 출력되는 특성 맵은 (28, 28, 32) 크기가 됩니다. + + + +<br/> + + + +특성 맵을 맥스 풀링을 해줍니다. 이때 풀링에 사용되는 커널은 2x2사이즈로 해서 절반으로 줄여줍니다. + +최종적으로 첫번째 (컨볼루션 + 풀링)층에서 나오는 특성 맵의 크기는 (14, 14, 32)가 됩니다. + + + +<br/> + + + +두 번째 합성곱 층에선 64개의 필터를 사용했습니다. + +동일한 커널 사이즈와 활성화 함수 패딩을 사용했으므로 나오는 특성 맵의 크기는 (14, 14, 64)가 됩니다. + +동일하게 맥스 풀링을 통과하면 특성 맵의 크기는 최종적으로 (7, 7, 64)가 됩니다. + + + +<br/> + + + +그 다음엔 Dense층을 놓고 클래스 개수에 맞는 10개의 확률값을 얻어야하기 때문에 Flatten()을 사용하여 + +(7, 7, 64) 특성 맵을 1차원 배열로 만들어줍니다. + + + +<br/> + + + +첫 번째 Dense층에서 활성화 함수 relu를 사용하는 뉴런 100개를 은닉층으로 두었습니다. + +3136개의 입력이 100개의 뉴런에 완전 연결이 되었으므로 굉장히 많은 가중치가 생성될 거 같네요. + +그래서 과적합을 피해보기 위해 은닉층 뒤에 Dropout을 사용해서 훈련 시에 40%의 뉴런을 끄도록 지정해 보았습니다. + +최종 출력층에는 10개의 출력을 가지고 softmax를 사용해야 10개의 확률을 얻을 수 있습니다. + + +#### 모델 summary + +각 층마다 출력되는 특성 맵과 출력 데이터의 크기와 파라미터 개수는 summary()함수를 통해 다시 한 번 확인해봅니다. + + + +```python +model.summary() +``` + +<pre> +Model: "sequential" +_________________________________________________________________ + Layer (type) Output Shape Param # +================================================================= + conv2d (Conv2D) (None, 28, 28, 32) 320 + + max_pooling2d (MaxPooling2D (None, 14, 14, 32) 0 + ) + + conv2d_1 (Conv2D) (None, 14, 14, 64) 18496 + + max_pooling2d_1 (MaxPooling (None, 7, 7, 64) 0 + 2D) + + flatten (Flatten) (None, 3136) 0 + + dense (Dense) (None, 100) 313700 + + dropout (Dropout) (None, 100) 0 + + dense_1 (Dense) (None, 10) 1010 + +================================================================= +Total params: 333,526 +Trainable params: 333,526 +Non-trainable params: 0 +_________________________________________________________________ +</pre> +summary 내용을 확인해 보았을 때, 첫번째 Dense층의 100개의 뉴런이 Flatten 층에서 나온 3136개의 입력 배열과 + +완전 연결이 되었으므로 굉장히 많은 가중치가 생긴 걸 확인할 수 있네요. + +그 위에 있는 합성곱층 2개의 가중치를 모두 더한것보다 훨씬 많은 것을 확인할 수 있는데요. + +이 점을 보았을 때 완전 연결층은 과적합이 쉽게 나타날 수 있고 + +반대로, 합성곱 층은 적은 개수의 파라미터로 효과적으로 이미지의 특징을 잘 잡아낼 수 있는 거 같습니다. + + +#### plot_model + +keras에는 plot_model()이라는 라이브러리가 있습니다. + +모델 구성 정보에 대한, summary()함수로 확인할 수 있는 정보들을 시각화해주는 라이브러리입니다. + +show_shapes=True로 주면 각 층의 input shape과 output shape을 나타내줍니다. + + + +```python +from tensorflow.keras.utils import plot_model + +plot_model(model, show_shapes=True) +``` + +<img src="data:image/png;base64,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r/2zGhoacObMGUyaNAkffPABpk+fjv79+6vd9rNphZVJqdtWaXdVsWrVKvz1119499138dlnnyExMRG7d+/GrFmzZNY9dOgQbGxsZKZjlfzq/v3332UG9XX2gOKDDz6Av78/Dh06BB6Ph3Xr1rW4TUedc135nFIGYwyXL18Gn89XGKg0R1PTaXcWNB096WiS6bTl0eijoffv38euXbuwdOlS7vLss6PgJfbt24f58+fjhx9+gFgsxtq1a9uk/WfTCiuTUret0u6qQiAQwNTUFEeOHIG1tTX27dsn9zL+jz/+CMYYPD09pcovXrzI3X5KSEho8/51NNYkVfKAAQOwdOlSVFRUICwsDIsWLWp2+7Y655gSuWu68jmljI0bNyItLQ179uyRut1JCOl62iSYqKioAPC/lL/A/+6VPn78mCuT3N6QrCdZJywsDE+fPsXly5dx6dIlPHnyBEKhEBUVFbhx4waSkpLw1ltvYciQIdi2bRuio6NbzOYmT3NphZVJqats2t3y8nKp/Xt2n1VNh/zNN98gKioKtbW1EIvF+Pvvv7njLfHLL7/giy++QG1tLQICAhAQEID9+/fjnXfeQUZGBubNm4fRo0fj2LFj3JdWYWEhLl68iPz8fGRkZHDZEpXVFml3W0NeqmRvb2/weDysX79e6mkLSR9zc3O5srY658rKygA0pvFVpCufU8/ue3V1tdS2Dx48gJeXF77++musX78eGzduVHiMCCFdhAqjNeX67bff2Lhx4xgA9tZbb7GcnByWmJjIJkyYwAAwFxcXlpWVxX777Tc2adIkBoAtXLiQ3blzhzHG2IoVK5hAIGDDhw9nQUFBLCoqiunp6TEnJycWFRXFBg8ezLy9vbkpQENDQxkA1r17d5nZAJvzzjvvMD6fz9577z32n//8hy1atIjNnTtXana+6upq5uXlxaysrNjRo0fZ4cOHmYuLC/v777+VXuf3339nM2fO5KZxjo+PZykpKczNzY0BYGPGjGFxcXEsKSmJDR06lAFgq1atYkVFRSwsLIwZGRkxAGznzp2straW/fjjj0xfX5+bQlrymjZtGisqKmJpaWlyl0uOUWlpKWOMsfv37zM7OzsGgA0dOpQtXryYzZ07l7366qvsm2++YdXV1Uofy/j4eObu7s4AsBdffJF9++23zU7drYiqo9EjIyPZlClTGAA2ffp0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ZhhMEJHZRERE4Ouvv4aIYOXKlbh48aK5m2QyW1tb7Nmzp9arc0sxYcIEizlxt2jRAmFhYUr3WGo+zB+KEtHvlqenJzw8PJTPlQczau6qe6xA1TNmBFGyTAwmiMhsnpyqm4iaJz7mICIiIpMwmCAiIiKTMJggIiIik/CdCTP4+OOPOaBKHd26dQvA/wakofo7e/YsAO7LmvB4a77Onj1rMH07NT6OgNnE+ONEzcndu3eRlJSE8ePHm7spREZzc3PDsmXLzN2M35NoBhNEVC0OY05ERuBw2kRERGQaBhNERERkEgYTREREZBIGE0RERGQSBhNERERkEgYTREREZBIGE0RERGQSBhNERERkEgYTREREZBIGE0RERGQSBhNERERkEgYTREREZBIGE0RERGQSBhNERERkEgYTREREZBIGE0RERGQSBhNERERkEgYTREREZBIGE0RERGQSBhNERERkEgYTREREZBIGE0RERGQSBhNERERkEgYTREREZBIGE0RERGQSBhNERERkEgYTREREZBIGE0RERGQSBhNERERkEgYTREREZBIGE0RERGQSBhNERERkEgYTREREZBJrczeAiCzDnTt34OnpieLiYiXt0aNH0Gg0GDBggF7ZwYMHIzw8vKmbSEQWisEEEQEAnnvuOTx+/BhXrlwxyMvNzdX77OPj01TNIqJmgI85iEjh6+sLa+uarzFUKhVmz57dRC0iouaAwQQRKWbNmoXS0tJq81UqFV544QX06NGjCVtFRJaOwQQRKRwdHTF8+HBYWVX906BWq+Hr69vErSIiS8dggoj0zJ07FyqVqsq8srIyvPbaa03cIiKydAwmiEjPzJkzq0xXq9UYN24cOnfu3MQtIiJLx2CCiPR07NgR48ePh1qtNsibO3euGVpERJaOwQQRGZgzZw5ERC/NysoK06dPN1OLiMiSMZggIgPTpk2DjY2N8tna2hoeHh7QaDRmbBURWSoGE0RkwM7ODl5eXkpAUVpaijlz5pi5VURkqRhMEFGVXn/9dZSUlAAAnnnmGUyZMsXMLSIiS8Vggoiq9PLLL6N169YAAG9vbzzzzDNmbhERWSrOzWEhbt26hf/85z/mbgaRnqFDhyI+Ph6Ojo6Iiooyd3OI9HDME8uhkidf2SaziIqKglarNXcziIiaDZ6+LEY0H3NYGBHhnwl/ABAZGWn2dpj7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"/> + +#### 모델 컴파일 및 훈련 + +옵티마이저는 'adam'을 사용했고, y데이터를 원 핫 인코딩 하지않고 그대로 사용하기 위해 + +손실 함수를 'sparse_categorical_crossentropy'로 지정했습니다. + + + +```python +# 모델 컴파일 +model.compile(optimizer='adam', + loss='sparse_categorical_crossentropy', + metrics=['acc']) +``` + +체크 포인트를 models 폴더 안에 'fashion_mnist_cnn_model.h5'로 지정하고 + +검증 셋의 점수가 2회 이상 증가했을 때 조기종료를 하고, 가장 손실이 낮았던 곳으로 되돌리기 위해 콜백을 지정했습니다. + + + +```python +# 모델 훈련 +from tensorflow.keras import callbacks +import os + +# models라는 폴더에 모델(h5파일)을 저장하려는데 해당 폴더가 없으면 만들어주기 +if not os.path.exists('./models/'): + os.mkdir('./models/') + +checkpoint_ch = callbacks.ModelCheckpoint('./models/fasion_mnist_cnn_model.h5') + +early_stopping_cb = callbacks.EarlyStopping(patience=2, restore_best_weights=True) + +history = model.fit(X_train, y_train, verbose=0, + epochs=20, validation_data=(X_val, y_val), + callbacks=[checkpoint_ch, early_stopping_cb]) +``` + +#### 훈련 시각화 + +훈련 셋과 검증 셋의 정확도와 손실을 시각화 해보겠습니다. + +아래와 같이 8번째 epoch(0부터)에서 훈련이 멈췄고 patience를 2로 주었기 때문에 + +6번째 에포크에서 가장 낮은 검증 손실을 얻을 수 있다고 확인할 수 있습니다. + +이 이후에는 검증 셋의 손실이 올라가기 때문에 과적합이 존재한다고 볼 수 있습니다. + + + +```python +import matplotlib.pyplot as plt + +loss = history.history['loss'] +val_loss = history.history['val_loss'] +acc = history.history['acc'] +val_acc = history.history['val_acc'] + +epochs = range(1, len(loss) + 1) +fig = plt.figure(figsize=(10, 5)) + +ax1 = fig.add_subplot(1, 2, 1) +ax1.plot(epochs, loss, color='blue', label='train_loss') +ax1.plot(epochs, val_loss, color='orange', label='val_loss') +ax1.set_title('train and val loss') +ax1.set_xlabel('epochs') +ax1.set_ylabel('loss') +ax1.legend() + +ax2 = fig.add_subplot(1, 2, 2) +ax2.plot(epochs, acc, color='green', label='train_acc') +ax2.plot(epochs, val_acc, color='red', label='val_acc') +ax2.set_title('train and val acc') +ax2.set_xlabel('epochs') +ax2.set_ylabel('acc') +ax2.legend() +``` + +<pre> +<matplotlib.legend.Legend at 0x7fa6ebe92820> +</pre> +<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAmcAAAFNCAYAAABFbcjcAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8QVMy6AAAACXBIWXMAAAsTAAALEwEAmpwYAABjsklEQVR4nO3dd3SU1dbA4d9OoffeO9JJIKEoghQFRATFhh0sXDsWLNcuyv2wISoIAjYUVBRQFAQVBESkJ/SOlAih955kf3+cAWIMMCHTkuxnrVmZedvZE4Y3e04VVcUYY4wxxoSGsGAHYIwxxhhjzrDkzBhjjDEmhFhyZowxxhgTQiw5M8YYY4wJIZacGWOMMcaEEEvOjDHGGGNCiCVn5oKJyFAReSEE4ughIrP8cN2XReSLs+xrLSIJvi7TGBNYOfk+ZkJXRLADMMEhIhuBe1T11wu9hqre57uIjDEmY+w+ZrIrqzkz6RIRS9yNMVma3cdMVmXJWQ4kIp8DlYAfROSQiDwlIlVEREXkbhHZDEzzHPuNiCSKyH4RmSki9VJd51MRec3zvLWIJIjIEyKyQ0S2iUjPc8TQU0RWishBEdkgIv9Jte+c1xKR4iIyQUQOiMg8oPo5ypksIg+l2bZYRLp5nr8rIls811ooIi0z+vv0XKeOiEwXkX0islxEuqTa10lEVnje698i0sezvYSI/Og5Z4+I/C4i9n/SGC/YfSzz9zERKeq5B+0Ukb2e5xVS7S8mIp+IyFbP/u9S7esqIvGeMteLSEdvyjTesT8EOZCq3g5sBq5W1QKq+kaq3ZcBdYAOntc/ATWBUsAiYNQ5Ll0GKAyUB+4GBotI0bMcuwPoDBQCegLviEhjL681GDgGlAXu8jzOZjRw86kXIlIXqAxM9GyaD0QDxTzHfiMiec5xvX8RkUjgB+Bn3O/pYWCUiNTyHPIR8B9VLQjUx/MHA3gCSABKAqWBZwFbT80YL9h9zCf3sTDgE8+1KgFHgUGp9n8O5APq4X5373jKbwqMBJ4EigCtgI1elGe8par2yIEP3H+ky1O9roJLDKqd45winmMKe15/Crzmed4a9x87ItXxO4DmXsbzHdD7fNcCwoGTQO1U+/4HzDrLdQsCh4HKntf9gI/PEcdeIMrz/GXgi7Mc1xpI8DxvCSQCYan2fwm87Hm+GfgPUCjNNfoC3wM1gv15sIc9suLD7mNnjcOr+1g650UDez3PywIpQNF0jvsQeCfY//7Z+WE1ZyatLaeeiEi4iPT3VFkf4Mw3oxJnOXe3qialen0EKJDegSJypYjM8TTn7QM6pbnu2a5VEjeQZUuqfZvO9mZU9SDu22V3z6bupPrW7GlyWOlp7tiH+5Z7tvd3NuWALaqakiam8p7n1+He3yYRmSEiF3u2vwmsA372NIk8k8FyjTHps/uYF/cxEcknIh+KyCbP72YmUEREwoGKwB5V3ZvOqRWB9ee7vrlwlpzlXGdrPku9/RagK3A57j97Fc92yUzBIpIbGAu8BZRW1SLAJC+vuxNIwt0cTql0nnO+BG72JEV5gd88cbQEngZuxH07LALs9zKO1LYCFdP0F6sE/A2gqvNVtSuuWeA7YIxn+0FVfUJVqwFXA4+LSLsMlm1MTmb3sczdx54AagHNVLUQrnkSz7lbgGIiUiSd87Zwjj5yJvMsOcu5tgPVznNMQeA4sBvX7+B/Pio7F5Abzw1KRK4E2ntzoqomA+OAlz3f+uoCd57ntEm4PhV9ga9T1XAVxN0gdwIRIvIiru9IRs3FNTk8JSKRItIal2x9JSK5RORWESmsqieBA0AygIh0FpEaIiKptidfQPnG5FR2H8vcfawgrul1n4gUA15KFeM2XF+9DzwDByJF5FTy9hHQU0TaiUiYiJQXkdpelmm8YMlZzvV/wPPiRgr2OcsxI3FV7X8DK4A5vijYU0X/CK4GaS/um+2EDFziIVzTQCKuv8gn5ynvOO5GeDmus+wpU3A3nzW493mMfzYzeEVVTwBdgCuBXcAHwB2quspzyO3ARk+zwX3AbZ7tNYFfgUPAn8AHqjo9o+Ubk4PZfSxz97GBuFq4Xbjfy+Q0+2/H9Y1bhesv96gnlnl4BkDgaulm4BJH4yPi6dxnjDHGGGNCgNWcGWOMMcaEEEvOjDHGGGNCiCVnxhhjjDEhxJIzY4wxxpgQYsmZMcYYY0wIiQh2AL5UokQJrVKlSrDDMMYEyMKFC3epaslgx+ELdv8yJuc52z0sWyVnVapUYcGCBcEOwxgTICJy1iVvshq7fxmT85ztHmbNmsYYY4wxIcSSM2OMMcaYEGLJmTHGGGNMCMlWfc6MCSUnT54kISGBY8eOBTuULC9PnjxUqFCByMjIYIcSUPYZypyc+rkxWZ8lZ8b4SUJCAgULFqRKlSqISLDDybJUld27d5OQkEDVqlWDHU5A2WfowuXkz43J+qxZ0xg/OXbsGMWLF7c/qpkkIhQvXjxH1h7ZZ+jC5eTPjcn6LDkzxo/sj6pv5OTfY05+75llvzuTVVlyZowxxhgTQiw5MyYb27dvHx988EGGz+vUqRP79u3L8Hk9evTg22+/zfB5JjQF+vNjjHFyZHL2++/w5ZfBjsIY/zvbH9fk5ORznjdp0iSKFCnip6hMVmGfH2O8o6rM3jKbsSvG+uR6OTI5GzYM+vQJdhTG+N8zzzzD+vXriY6OpkmTJrRp04ZbbrmFBg0aAHDNNdcQExNDvXr1GDZs2OnzqlSpwq5du9i4cSN16tTh3nvvpV69erRv356jR496VfbUqVNp1KgRDRo04K677uL48eOnY6pbty4NGzakj+c/4jfffEP9+vWJioqiVatWPv4tXBgR6Sgiq0VknYg8k87+oiIyXkSWiMg8EamfZn+4iMSJyI+Bi9q3Av35GT58OE2aNCEqKorrrruOI0eOALB9+3auvfZaoqKiiIqKYvbs2QCMHDmShg0bEhUVxe233+7H34Qx6TuZfJKvln1F84+a0+LjFrzw2wuoaqavmyOn0oiJgS++gMREKFMm2NGYnODRRyE+3rfXjI6GgQPPfUz//v1ZtmwZ8fHxTJ8+nauuuoply5adnlrg448/plixYhw9epQmTZpw3XXXUbx48X9cY+3atXz55ZcMHz6cG2+8kbFjx3Lbbbeds9xjx47Ro0cPpk6dykUXXcQdd9zBkCFDuOOOOxg/fjyrVq1CRE43ffXt25cpU6ZQvnz5kGgOE5FwYDBwBZAAzBeRCaq6ItVhzwLxqnqtiNT2HN8u1f7ewEqgkC9ienTyo8QnxvviUqdFl4lmYMeBZ90f6M9Pt27duPfeewF4/vnn+eijj3j44Yd55JFHuOyyyxg/fjzJyckcOnSI5cuX069fP/744w9KlCjBnj17fPNLMcYL+47tY/jC4bw/7322HNhCjWI1GHTlIO6MvtMnA1FyZM1ZTIz7uXBhcOMwJtCaNm36jzmf3nvvPaKiomjevDlbtmxh7dq1/zqnatWqREdHAxATE8PGjRvPW87q1aupWrUqF110EQB33nknM2fOpFChQuTJk4d77rmHcePGkS9fPgBatGhBjx49GD58+HmbzAKkKbBOVTeo6gngK6BrmmPqAlMBVHUVUEVESgOISAXgKmBE4EL2P39/fpYtW0bLli1p0KABo0aNYvny5QBMmzaN+++/H4Dw8HAKFy7MtGnTuP766ylRogQAxYoV89G7NObs1u9ZzyM/PUKFARV46tenqF6sOt93/57VD63mwaYPUiBXAZ+UkyNrzho1AhGXnF11VbCjMTnB+Wq4AiV//vynn0+fPp1ff/2VP//8k3z58tG6det054TKnTv36efh4eFeNWuerVo/IiKCefPmMXXqVL766isGDRrEtGnTGDp0KHPnzmXixIlER0cTHx//rxqYACsPbEn1OgFoluaYxUA3YJaINAUqAxWA7cBA4Cmg4LkKEZFeQC+ASpUqnTOgc9VwBYq/Pz89evTgu+++Iyoqik8//ZTp06ef9VhVtakyTECoKrM2z2LAnAF8v+p7IsIi6F6/O481f4xGZRv5pcwcWXNWoADUqmU1Zyb7K1iwIAcPHkx33/79+ylatCj58uVj1apVzJkzx2fl1q5dm40bN7Ju3ToAPv/8cy677DIOHTrE/v376dSpEwMHDiTe09a7fv16mjVrRt++fSlRogRbtmw5x9UDIr2/+mkzzv5AURGJBx4G4oAkEekM7FDV895hVHWYqsaqamzJkiUzG7PPBfrzc/DgQcqWLcvJkycZNWrU6e3t2rVjyJAhgBuMcODAAdq1a8eYMWPYvXs3gDVrGp87mXySUUtG0WR4E1p92oqZm2by30v/y8ZHNzLy2pF+S8wgh9acgWvaPMeXMmOyheLFi9OiRQvq169P3rx5KV269Ol9HTt2ZOjQoTRs2JBatWrRvHlzn5WbJ08ePvnkE2644QaSkpJo0qQJ9913H3v27KFr164cO3YMVeWdd94B4Mknn2Tt2rWoKu3atSMqKspnsVygBKBiqtcVgK2pD1DVA0BPAHFVOH95Ht2BLiLSCcgDFBKRL1T13B31QlCgPz+vvvoqzZo1o3LlyjRo0OB0Yvjuu+/Sq1cvPvroI8LDwxkyZAgXX3wxzz33HJdddhnh4eE0atSITz/9NNMxGLPn6B6GLRzGoHmD+Pvg39QqXouhVw3l9qjbyReZLyAxiC9GFYSK2NhYXbBggVfHvvMOPP64GxSQ6n5jjM+sXLmSOnXqBDuMbCO936eILFTVWF+XJSIRwBpcB/+/gfnALaq6PNUxRYAjqnpCRO4FWqrqHWmu0xroo6qdz1dmevcv+wxlnv0OjbfW7F7Du3Pe5dPFn3Lk5BHaVW3H4xc/TscaHQkT/zQ0nu0elqNrzsA1bXbqFNxYjDGhRVWTROQhYAoQDnysqstF5D7P/qFAHWCkiCQDK4C7gxawMeaCqCozNs1gwJ8D+HHNj0SGR3JLg1t4tNmjRJUJXg1+jk3OUg8KsOTMmIx58MEH+eOPP/6xrXfv3vTs2TNIEfmeqk4CJqXZNjTV8z+Bmue5xnRguh/Cy9JywufHhLYTySf4etnXDJgzgPjEeErkK8HzrZ7ngSYPUKZA8OfYyrHJWcGCcNFFNijAmAsxePDgYIdgsjD7/JhgSTyUyMdxHzNo3iC2HdpG3ZJ1GX71cG5tcCt5I/MGO7zTcmxyBq5pc+bMYEdhjDHGGH/YtG8TMzfN5PfNvzNz00xW714NQPvq7fmk6ye0r94+JKdkyfHJ2ejRsGMHlCoV7GiMMcYYc6FUlVW7Vp1OxH7f/Dub928GoHDuwrSs3JK7Gt1F54s6U7dk3SBHe245PjkD17R55ZXBjcUYY4wx3ktOSWbx9sWnE7HfN/3OziM7ASidvzStKrfiyUuepGWlltQvVZ/wsPAgR+y9HJ2cNfLMH2fJmTHGGBPajicdZ/7W+fy+6Xdmbp7J7C2zOXD8AABVi1TlyppX0qpSK1pVbkWNYjVCsrnSWzk6OStUyAYFGJNagQIFOHToULr7Nm7cSOfOnVm2bFmAozJZxbk+P8Zk1KETh/hzy5+na8bmJMzhePJxAOqWrMst9W+hZeWWtKzUkoqFK57nalmLX5MzEekIvIubJ2iEqvZPs7818D1uVm2Acara15tzfSUmBmbN8seVjTHGGJMR+47tY9C8QUxYPYFF2xaRrMmESRiNyjTigSYP0KpyKy6tdCkl8pUIdqh+5bfkTETCgcHAFbilUOaLyARVXZHm0N/Tzp6dgXMzLSYGvvwSdu6EEFzazmQXCx+FvfG+vWbRaIgZeM5Dnn76aSpXrswDDzwAwMsvv4yIMHPmTPbu3cvJkyd57bXX6Nq1a4aKPnbsGPfffz8LFiwgIiKCAQMG0KZNG5YvX07Pnj05ceIEKSkpjB07lnLlynHjjTeSkJBAcnIyL7zwAjfddNMFvukc7NFHwbMWqc9ER8PAgWfd7cvPz6FDh+jatWu6540cOZK33noLEaFhw4Z8/vnnbN++nfvuu48NGzYAMGTIEC655JJMv2UTmg4cP8B7c9/j7T/fZt+xfVxS8RKebvE0rSq34uKKF1Mod6FghxhQ/qw5awqsU9UNACLyFdAVN5O2P8/NkMaN3c9Fi6BDB19f3Zjg6t69O48++ujpP65jxoxh8uTJPPbYYxQqVIhdu3bRvHlzunTpkqH+GafmqVq6dCmrVq2iffv2rFmzhqFDh9K7d29uvfVWTpw4QXJyMpMmTaJcuXJMnDgRcAtmm6zBl5+fPHnyMH78+H+dt2LFCvr168cff/xBiRIlTi9g/sgjj3DZZZcxfvx4kpOTrbk0mzp04hDvz32ft/58iz1H99ClVhdevuxlvy4qnhX4MzkrD2xJ9ToBaJbOcReLyGLcosJ9PGvXeXtupp1KzhYutOTM+NF5arj8pVGjRuzYsYOtW7eyc+dOihYtStmyZXnssceYOXMmYWFh/P3332zfvp0yZbyfFXvWrFk8/PDDANSuXZvKlSuzZs0aLr74Yvr160dCQgLdunWjZs2aNGjQgD59+vD000/TuXNnWrZs6a+3m72do4bLX3z5+VFVnn322X+dN23aNK6//npKlHDNVMWKFQNg2rRpjBw5EoDw8HAKFy7s3zdrAurwicN8MP8D3pj9BruO7KJTzU680voVYsv5fKncLMmfyVl6X6PSrrK+CKisqodEpBPwHW45FG/OdYWI9AJ6AVSqVCnDQRYuDDVq2KAAk31df/31fPvttyQmJtK9e3dGjRrFzp07WbhwIZGRkVSpUoVjx45l6Jqq6f535JZbbqFZs2ZMnDiRDh06MGLECNq2bcvChQuZNGkS//3vf2nfvj0vvviiL96aCQBffX7Odp6qZulRdSZjjpw8wtAFQ3n9j9fZcXgHHap34JXWr9Csgl/qX7Is/yyz7iQAqYdPVMDVjp2mqgdU9ZDn+SQgUkRKeHNuqmsMU9VYVY0teYGdxmJiLDkz2Vf37t356quv+Pbbb7n++uvZv38/pUqVIjIykt9++41NmzZl+JqtWrVi1KhRAKxZs4bNmzdTq1YtNmzYQLVq1XjkkUfo0qULS5YsYevWreTLl4/bbruNPn36sGjRIl+/ReNHvvr8nO28du3aMWbMGHbv3g1wulmzXbt2DBkyBIDk5GQOHDjgh3dnAuVY0jHem/se1d+rzhM/P0GDUg2Y1XMWk2+bbIlZOvyZnM0HaopIVRHJBXQHJqQ+QETKiOcrk4g09cSz25tzfSkmBjZtAs+9wZhspV69ehw8eJDy5ctTtmxZbr31VhYsWEBsbCyjRo2idu3aGb7mAw88QHJyMg0aNOCmm27i008/JXfu3Hz99dfUr1+f6OhoVq1axR133MHSpUtp2rQp0dHR9OvXj+eff94P79L4i68+P2c7r169ejz33HNcdtllREVF8fjjjwPw7rvv8ttvv9GgQQNiYmJYvny5396j8Z/jScf5YP4HVH+vOr0n96ZW8VrM6DGDX+/4lRaVWgQ7vJAlZ2ue8MnFXVPlQNx0GB+raj8RuQ9AVYeKyEPA/UAScBR4XFVnn+3c85UXGxurCxYsyHCc06ZBu3YwZQq0b5/h041J18qVK6lTp06ww8g20vt9ishCVc0WnVTSu3/ZZyjz7HcYHCeST/BJ3Cf0+70fWw5s4dJKl9K3dV/aVG0T7NBCytnuYX6d58zTVDkpzbahqZ4PAgZ5e66/pB4UYMmZMcYYc2FOJp9k5OKRvDrzVTbt30TzCs35qMtHXF7tcutbmAE5eoWAU4oUgerVrd+ZMeCmx7j99tv/sS137tzMnTs3SBGZrMQ+PzlTUkoSo5aMou/MvmzYu4Em5Zow5KohdKzR0ZKyC2DJmUdMDMybF+wojAm+Bg0aEO/ryU5NjmGfn5wlOSWZL5d9Sd8ZfVm7Zy2NyjTih5t/4KqaV1lSlgmWnHnExMCYMW5QQPHiwY7GZBc2TYBv+LNvbKizz9CFy8mfG39L0RTGLB/DKzNeYdWuVTQs3ZDxN42na62u9nn1AUvOPGJi3M9Fi+CKK4Ibi8ke8uTJw+7duylevLjdrDJBVdm9ezd58uQJdigBZ5+hC5eTPzf+tGnfJr5Y8gWfLf6MtXvWUq9kPb694VuurXMtYeLPCSByFkvOPFIPCrDkzPhChQoVSEhIYOfOncEOJcvLkycPFSpUCHYYAWefoczJqZ8bXztw/ABjV4xl5JKRTN84HYBWlVvxaptXuaHeDZaU+YElZx5Fi0K1ajYowPhOZGQkVatWDXYYJguzz5AJluSUZH7d8Csjl4xk/MrxHE06So1iNejbui+3NbyNqkXtc+lPlpylEhMDFzBNmjHGGJMtLN2+lJGLRzJq6Si2HdpGkTxFuDPqTu6IuoPmFZpb83qAWHKWSkwMfPMN7NkDnrV3jTHGmGxt+6HtjF46mpFLRhKfGE9EWASdanbijoZ30PmizuSOyB3sEHMcS85SST0o4PLLgxuLMcYY4y9HTx7lhzU/MHLxSCavm0yyJhNbLpb3Or5H9/rdKZn/wtaqNr5hyVkqqQcFWHJmjDEmO1FVZm2excjFI/lmxTfsP76fCoUq8OQlT3J71O3ULVk32CEaD0vOUilWDKpWtUEBxhhjso91e9bx+eLP+XzJ5/y17y/yR+bnurrXcUfDO2hdpTXhYeHBDtHZuRMmT3YPVYiNdY9GjaBgwWBHF1CWnKXRuLElZ8YYY7K2FE3hiyVf8OHCD5m9ZTaC0K5aO15p/QrX1rmWArkKBDtESElx/YgmToRJk2D+fJeUlS4NkZHw5ZfuOBGoVetMshYT4xK2/PmDG78fWXKWRkwMjB0Le/e66TWMMcaYrGT1rtX0+rEXMzfNpE6JOvRv159bG95KhUIhMOfbvn3w888uGfvpJ9ixwyVfzZrBK69Ap04u8QoLg+3bXW3JggXuMW0afPGFu05YGNSpcyZZi42FqCjIly9w70UV9u+HrVvPPHLnhptuyvSlLTlLI/WggHbtghuLMSZ4RKQj8C4QDoxQ1f5p9hcFPgaqA8eAu1R1mYhUBEYCZYAUYJiqvhvQ4E2OdDL5JG/OfpO+M/qSNzIvH3f5mB7RPYI7/YUqLFvmkrGJE2H2bEhOdrUfHTu6ZKxjRyhR4t/nli7t9nfqdGbb1q1nEraFC12C99lnbl94ONSte6aGLTYWGjaEjK4SoQoHD7qytm37Z/KV9nHs2D/PbdjQkjN/sOTMGCMi4cBg4AogAZgvIhNUdUWqw54F4lX1WhGp7Tm+HZAEPKGqi0SkILBQRH5Jc64xPrVg6wLumXAPi7cv5oa6N/Dele9RpkCZ4ARz6BBMneoSskmTICHBbW/UCJ55xiVbTZtCxAWkIOXKucfVV7vXqvD332eStQUL4Icf4JNP3P6ICKhf/581bIULn0muzpZ8HT7877Lz54fy5V35zZu7n2XLnonp1GsfsOQsjeLFoXJl63dmTA7XFFinqhsAROQroCuQOsGqC/wfgKquEpEqIlJaVbcB2zzbD4rISqB8mnON8YkjJ4/w4m8v8s6cdyhToAzf3fQdXWt3DWwQqrB27ZlkbMYMOHHCdeK/4gp4+WW48kqXvPiaCFSo4B7XXHMmns2b/9kkOnYsjBiR/jXy5j2TXDVuDJ07/zPZOvU8gIMSLDlLR0yMJWfG5HDlgS2pXicAzdIcsxjoBswSkaZAZaACsP3UASJSBWgEzPVnsCZnmrphKr1+7MWGvRvo1bgXb1zxBoXzFA5M4ceOwfTpZxKy9evd9jp14JFHXO1YixaQK1dg4klNxNWyVK4M3bq5baqwcaNL1I4ePVMDVrasq0kLsZUPLDlLR0wMjBvn+vkVDtDn3BgTUtK7U2ua1/2Bd0UkHlgKxOGaNN0FRAoAY4FHVfVAuoWI9AJ6AVSqVCnzUZscYe/RvTzx8xN8Ev8JNYvVZPqd07msymWBC+DXX+HOO13zX9680LYtPP64qx0L1bVgRVxsoRpfGpacpSN1v7M2bYIbizEmKBKAiqleVwC2pj7Ak3D1BBDX4/ovzwMRicQlZqNUddzZClHVYcAwgNjY2LTJnzH/oKqMXTmWhyY9xK4ju3imxTO8eNmL5I3MG5gAjh+H556Dt992NWTDhrnELG+Ays9BLDlLx6nkbOFCS86MyaHmAzVFpCrwN9AduCX1ASJSBDiiqieAe4CZqnrAk6h9BKxU1QGBDdtkV1sPbuXBSQ/y3arvaFy2MT/d+hONyjYKXACrVsHNN0N8PNx/P7z1VmCnrchhLDlLR4kSUKmS9TszJqdS1SQReQiYgptK42NVXS4i93n2DwXqACNFJBnX2f9uz+ktgNuBpZ4mT4BnVXVSIN+DyR5SNIURi0bw5C9PciL5BG9c/gaPXfwYEWEB+vOt6mrIHnvMjVb8/nvo0iUwZedglpydhQ0KMCZn8yRTk9JsG5rq+Z9AzXTOm0X6fdaMyZC1u9dy7w/3MmPTDNpUacOwq4dRo1iNwAWwaxfcc49LyK64ws0n5qOpIsy5hQU7gFAVE+NGBu/fH+xIjDHG5CQnk0/Sf1Z/GgxpQHxiPMOvHs7UO6YGNjH75Rc3oepPP8GAAW69S0vMAsZqzs7iVL+zuDho3TqooRhjjMkhFm1bxN0T7iY+MZ5udbox6MpBlC0YwKQobaf/SZMgOjpw5RvAzzVnItJRRFaLyDoReeYcxzURkWQRuT7Vto0islRE4kVkgT/jTE/qQQHGGGOMPx05eYSnfnmKpsObkngokbE3jmXsjWMDm5itWuVmvn/7bdfpf8ECS8yCxG81Z14uf3LquNdxHW/TaqOqu/wV47mULAkVK1pyZowxxr+m/TWNXj/0Yv3e9dzT6B7ebP8mRfIUCVwA1uk/5PizWdOb5U8AHsbNB9TEj7FcEBsUYIwxxl/+Wj2XTY/cQdzBNcQ2Kcfwh3+hTY3LAxuEdfoPSf5s1kxv+ZPyqQ8QkfLAtcBQ/k2Bn0VkoWcW7YCLiYE1a+BAunN7G2OMMRk3N2Eu/32tDZFNmnPx1DX0XhDOV+9tpc3FN8Ndd7lE6cgR/wdinf5Dlj+TM2+WPxkIPK2qyekc20JVGwNXAg+KSKt0CxHpJSILRGTBzp07MxVwWqkHBRhjjDEXKkVT+HHNj1z2SSs+79WcV16eTt4CRTgwfQoRu/bAmDHQvr1bO/Caa9yEm9dcA598Aj7+28bx49CnjyuvSBGYO9c1aYbZBA6hwp/Nmudd/gSIBb5yE2pTAugkIkmq+p2qbgVQ1R0iMh7XTDozbSH+XP6kcWP3c+FCuCyAy5YZY4zJHo4nHWf00tG8OftNNm1dyaif8nFNHCR16kjxL0ZD0aLuwBtucI+TJ2HmTPjuO1eD9v33Lmlq0QK6dnWPGpmYUmPlSrjlFpvpP8T5M00+vfyJiOTCLX8yIfUBqlpVVauoahXgW+ABVf1ORPKLSEEAEckPtAeW+THWdJUu7Raut35nxhhjMmLfsX28Put1qr5blbsm3EXNnSn8/XUFui4+Bv36EfHDxDOJWWqRkdCuHbz/Pmza5BZ5fv5517+mTx+oWRPq13fTXcybBykp3gWkCkOHuiahhASX9H3wgSVmIcpvNWdeLn9yNqWB8Z4atQhgtKpO9les52KDAowxxngr4UACA+cMZNjCYRw8cZDLq13OpDx3E/Xfd5HcuWHKFLjcy07/ItCokXu88gps3HimNu311+F//4Ny5dzIymuucZNy5s797+tYp/8sR1R92hIYVLGxsbpggW+nROvbF15+2a0UULCgTy9tjMkkEVmoqrHBjsMX/HH/MoGzdPtS3vrzLUYvHY2qcmO9G3myyaM0em+MmzesWTP45hs3R5Mv7NkDEye6hGvyZDh82P2RuvJKl6hdeaXrT/bLL3DnnbB7N/TvD717W9+yEHK2e5itEHAeMTGuNjguDlqlOyTBGGNMTqSqTN84nTdmv8HkdZPJH5mfB5s8yKPNH6XK0dxw003w++/w4INuNGSuXL4rvFgxuP129zh2DKZOdYnahAlucEFEhPsDNneuzfSfBVn6fB6nRmwuWhTcOIwxxoSGpJQkvl72NU2GN6HtyLYs2raI19q8xubHNjOw40CqLN3iRpQtXAhffAGDBvk2MUsrTx646io3kezWrTB7NjzxBJw4AQ8/bDP9Z0FWc3YeZcq4Jn3rd2aMMTnb4ROH+ST+Ewb8OYC/9v3FRcUv4sPOH3JH1B3kicjjmlkGDICnnoLq1V2TYv36gQ0yLAwuvtg9+vcPbNnGZyw584INCjDGmJxrx+EdDJo3iMHzB7Pn6B4uqXgJAzoMoEutLoSJpwHqwAE3gezYsdCtm5ufrFCh4AZusixLzrwQEwM//giHDkGBAsGOxhhjTCDsOrKLN/54g0HzBnEs6RhdanXhyUuepEWlFv88cPlyuO46WLcO3nzTNSlKevOwG+MdS868cGpQQHw8XHppsKMxxhhzmir88AMMH+5GQrZt66aUKFHigi+579g+3p79NgPnDuTIySPc2uBWnm35LLVL1P73wV9+6aapKFjQdcq3GcuND1hy5oVTgwIWLrTkzBhjQkJKiptF/9VX3Tfn8uVh+nQYMsTtb9jQJWpt2rih9kWKnPeSB48f5N257/LW7LfYf3w/N9a7kZcve5k6Jev8++ATJ1wN2aBB7g/D11+7DsrG+IAlZ14oW9Y9rN+ZMcYEWXIyfPstvPYaLFsGF10EI0fCzTe7WrSFC2HaNPcYOhQGDnSd5Bs3PpOsXXrpP/qoHDl5hMHzBvP6H6+z++huutbqyiutXyGqTFT6MSQkwI03wp9/ujUpX3/dzexvjI9YcuYlGxRgjDFBlJTkaqdeew1WrXJzd40e7ZKk8PAzxzVv7h7PPusW+J4zB377zSVr77wDb7zh5gBr2pSk1q2YUP4Qj+0fw+YTO+hYoyN9W/elSfkmZ49j6lSXCB496uYTu+EG/793k+NYcualmBg3h9/hw5A/f7CjMcaYHCIpCUaNgn79YO1aNzXFmDGuA/75ZrrPndv1AbvsMrfUy5Ej8McfJE/9lV0Tv6F4//50S4HOEcKRJo0pEnYJVDkBJU/8e16ylBRXQ/b881CrFowbB7XT6YNmjA/YJLReiolx/zfj44MdiTHG5AAnTsBHH7lEqEcP96143DhYvNjVVl3AEkRJeXLxacm/qVn2G8pc/xcd323C4hH9yPXIYxQ5pvDSS67Js2hR6NjRJWPz57ulj6691tXG3XCDW3DcEjPjR1Zz5qXUgwJatDj3scYYYy7Q8ePw6afwf/8HmzZBbKzrN9a58wVPT5GiKXy97GtenvEya3avoXHZxgzuNJiONToiqa+5Zw/MmOGaQH/7DZ55xm0XcU2n770HDz1k02QYv7PkzEvlyrnVAqzfmTHG+MGxY66mrH9/1+G+WTM38rJjxwtOhlSV8avG8+JvL7J853Lql6rP+JvG07VW138mZacUK+ZqyK691r1OTHQjQBcudM2ozZtf+PszJgMsOcsAGxRgjDE+duSIWxPyjTdg2zbXrPjxx3D55ZlKyiatncSL019k0bZF1Cpeiy+v+5Ib6914ZkZ/b5QpA927u4cxAWTJWQY0bgw//WSDAowxJtMOH3Y1Y2++CTt2uIljR41yPzORlE39ayov/PYCcxLmULVIVT7t+im3NryViDD7c2eyDvu0ZsCpQQGLF8MllwQ7GmOMyYIOHoTBg+Htt2HXLldD9sILbqLYTPhj8x88N+05ZmyaQYVCFRjWeRg9onsQGW7zj5msx5KzDEg9KMCSM2OMyYDDh908Y++84zred+zokjIf3Ezf+fMdHv/5ccoUKMN7Hd/j3ph7yRORxwdBGxMcNpVGBpQvD6VKWb8zY3ICEekoIqtFZJ2IPJPO/qIiMl5ElojIPBGp7+25OYqqmzy2du0zydjcua6PSCYTM1XlmV+f4fGfH+e6Otex/pH1PNzsYUvMTJZnyVkGiNigAGNyAhEJBwYDVwJ1gZtFpG6aw54F4lW1IXAH8G4Gzs0ZlixxyyV17+4WIv/9d7dIedOmmb50UkoSd0+4m9f/eJ37Yu7j6+u/Jl9kPh8EbUzwWXKWQTExsGKFG2BkjMm2mgLrVHWDqp4AvgK6pjmmLjAVQFVXAVVEpLSX52Zve/a4+cAaNXLrXw4dCgsWuJGYPnDk5BGu/fpaPon/hJcue4kPrvqA8LDw859oTBZhyVkGpR4UYIzJtsoDW1K9TvBsS20x0A1ARJoClYEKXp6bPSUnu0SsZk03EvOBB2DNGvjPf/65/mUm7D26l/aft2fimol80OkDXm79cvpzlhmThVlylkGnBgUsWhTcOIwxfpXeX3tN87o/UFRE4oGHgTggyctzXSEivURkgYgs2LlzZybCDQG//+5ukPffDw0aQFwcvP++m9jVR/4+8DctP2nJ/K3z+fr6r7m/yf0+u7YxocRGa2ZQhQpQsqT1OzMmm0sAKqZ6XQHYmvoAVT0A9AQQV3Xzl+eR73znprrGMGAYQGxsbLoJXMhLSICnnoIvv4SKFV3n/xtu8PkSR6t2raLDFx3Ye3QvP936E22rtvXp9Y0JJVZzlkE2KMCYHGE+UFNEqopILqA7MCH1ASJSxLMP4B5gpidhO++52cKxY/C//7mFyceNcyMxV66EG2/0eWI27+95XPrxpRxLOsb0HtMtMTPZnl+TM2+Hk4tIExFJFpHrM3puMMTEwPLlcPRosCMxxviDqiYBDwFTgJXAGFVdLiL3ich9nsPqAMtFZBVuZGbvc50b6PfgN6puxGX9+vDcc9Chg0vK+vb1y9IpU9ZNoe1nbSmcpzB/3PUHjcs29nkZxoQavzVrphpOfgWuiWC+iExQ1RXpHPc67kaWoXODJSbG9XtdssStzWuMyX5UdRIwKc22oame/wnU9PbcbGH1anj0UZg8GerUgZ9/hiuu8Ftxo5eO5s7v7qReyXpMvm0yZQqU8VtZxoQSf9aceTuc/GFgLLDjAs4NitQrBRhjTLZ34AA8+aSrLZs9283yv3ixXxOzd+e8y63jbqVFxRbM6DHDEjOTo/gzOTvvcHIRKQ9cCwzln0J6KHrFim4+RUvOjDHZWkoKfPYZXHSRWwvzzjth7VpXexbpnzUrVZVnpz7Lo1MepVudbky+bTKF8xT2S1nGhCp/jtb0Zjj5QOBpVU1OM09NhoaiA70AKlWqlPEoL4ANCjDGZHvz58PDD7ullpo1c/3MmjTxa5FJKUn854f/8HH8x/Rq3MsmlzU5lj9rzs47FB2IBb4SkY3A9cAHInKNl+cCbii6qsaqamzJkiV9FPr5nRoUcOxYwIo0xhj/27ED7rnHJWQbN7qas9mz/Z6YHT15lOvGXMfH8R/zQqsXGNp5qCVmJsfyZ83Z6eHkwN+44eS3pD5AVaueei4inwI/qup3IhJxvnODLSYGkpLcoAAfLBNnjDHBt3y5W2Lp0CF44gk3PUahQn4vdu/RvXT5qgt/bP6DQVcO4sGmD/q9TGNCmd+SM1VNEpFTw8nDgY9PDUX37E/bz+y85/or1guRelCAJWfGmCxP1S23FBYGS5dC7doBKXbrwa10+KIDq3et5qvrv+LGejcGpFxjQplfVwg431D0NNt7nO/cUFKpkluVxPqdGWOyhdGjYeZM+PDDgCVma3avof3n7dl9dDc/3foT7aq1C0i5xoQ6W77pAtmgAGNMtnHgAPTp4/qV3X13QIqc//d8Oo3uhCBMv3M6MeViAlKuMVmBLd+UCTExsGyZDQowxmRxL78M27fD4MEQ7v9O+L+s/4U2n7WhQK4C/HHXH5aYGZOGJWeZcGpQwNKlwY7EGGMu0NKl8N570KuX30dkAny59EuuGn0V1YtVZ/Zds6lZPN1FFozJ0Sw5ywRbKcAYk6WpwoMPQuHC0K+f34t7f+773DLuFi6ueDEzesygbMGyfi/TmKzI+pxlQpUqULSoJWfGmCxq1Cj4/XcYNgyKF/drUd8s/4ZHJj/CNbWv4cvrviRPRB6/lmdMVmbJWSbYoABjTJa1f79bL7NpU78PAtiyfwu9fuxF0/JNGXP9GCLD/bP0kzHZhTVrZtKpQQHHjwc7EmOMyYDUgwDC/PenIDklmdvH305SShKjuo2yxMwYL1hylkkxMXDypA0KMMZkIUuWwPvvw3/+A7Gxfi3qzdlvMmPTDN6/8n1qFKvh17KMyS4sOcskGxRgjMlSTg0CKFLE74MAFmxdwAu/vcANdW/gzqg7/VqWMdmJ9TnLpKpV3aCARYuCHYkxxnjhiy9g1iwYPtwtc+Inh04c4paxt1CmQBk+7PwhIuK3sozJbiw5yyQRaNzYas6MMVlA6kEAd93l16Iem/wY6/asY9qd0yiat6hfyzImu7FmTR+IiXF9zk6cCHYkxhhzDi+9BDt2wAcf+HUQwNgVYxkRN4KnWzxN6yqt/VaOMdmVJWc+EBPjErNly4IdiTHGnMXixW4QwH33neks6wcJBxK494d7iS0XyyttXvFbOcZkZ5ac+YANCjDGhLRTgwCKFYPXXvNbMSmawp3f3cnx5OOM6jaKXOG5/FaWMdmZJWc+UK2aG/hkyZkxJiR9/jn88Qf07+/XQQBvz36baX9N472O73FR8Yv8Vo4x2Z0lZz5ggwKMMSFr3z43CKBZM+jZ02/FLNq2iOemPUe3Ot24q5F/BxsYk91ZcuYjMTFuXkcbFGCMCSkvvQQ7d/p1EMDhE4e5ZewtlMpfiuFXD7dpM4zJJEvOfKRxY5eYLV8e7EiMMcZj8WIYNAjuv9/dpPzkiZ+fYM3uNYy8diTF8vqv2dSYnMKSMx+xQQHGmJCSkhKQQQDfrfqODxd+yJOXPEnbqm39Vo4xOYklZz5SvToUKmTJmTEmRJwaBPD6624ZEz/YenAr90y4h8ZlG/Nq21f9UoYxOZElZz4SFmaDAowxIeLUIIDmzaFHD78UkaIp9PiuB0dOHrFpM4zxMUvOfOjUoICTJ4MdiTEmR3vxRdi9GwYP9tsggIFzBvLLhl8Y2HEgtUvU9ksZxuRUlpz5UEwMHD9ugwKMyQ5EpKOIrBaRdSLyTDr7C4vIDyKyWESWi0jPVPse82xbJiJfikiegAUeH++SMj8OAohPjOe/U/9L11pdubfxvX4pw5iczJIzH7JBAcZkDyISDgwGrgTqAjeLSN00hz0IrFDVKKA18LaI5BKR8sAjQKyq1gfCge4BCfzUIIDixeFV//QBO3LyCLeMvYXieYszossImzbDGD/wa3LmxTfPriKyRETiRWSBiFyaat9GEVl6ap8/4/SVGjWgYEFLzozJBpoC61R1g6qeAL4CuqY5RoGC4rKTAsAeIMmzLwLIKyIRQD5ga0CiHjkSZs+GN97w2yCAJ39+kpW7VvLZNZ9RIl8Jv5RhTE4X4a8Lp/rmeQWQAMwXkQmquiLVYVOBCaqqItIQGAOk7rzQRlV3+StGXzs1KGBBlkgljTHnUB7Ykup1AtAszTGDgAm4xKsgcJOqpgB/i8hbwGbgKPCzqv6cXiEi0gvoBVCpUqXMRbx3Lzz1FFx8MdxxR+audRY/rvmRDxZ8wBMXP8EV1a/wSxnGGP/WnJ33m6eqHlJV9bzMj/smmqW1awfz58OUKcGOxBiTCem11aW9P3UA4oFyQDQwSEQKiUhR3L2uqmdffhG5Lb1CVHWYqsaqamzJkiUzF7GfBwEkHkqk5/c9iS4TTb+2/Xx+fWPMGV79DxaR3p6bjojIRyKySETan+e09L55lk/n2teKyCpgIpB6QTYFfhaRhZ5vl1lCnz5Qt65bwm737mBHY4y5QAlAxVSvK/DvpsmewDh11gF/4Wr+Lwf+UtWdqnoSGAdc4tdo4+Lc8kwPPACNGvn88qemzTh04hCju40md0Run5dhjDnD269Xd6nqAaA9UBJ3U+p/nnO8+eaJqo5X1drANUDqHqwtVLUxrkPugyLSKt1CRHp5+qst2Llz5/nfiZ/lzQtffAG7drnBUprl6wKNydo8XwALp3pdRESuOc9p84GaIlJVRHLhOvRPSHPMZqCd55qlgVrABs/25iKSz9MfrR2w0idvJj0BGATw/tz3mbJ+CgPaD6BOyTp+KcMYc4a3ydmpRKsT8ImqLib95Cs1b755nqaqM4HqIlLC83qr5+cOYDyumTS983zXLOAjjRrBK6/AN9/A6NHBjsaYHO8lVd1/6oWq7gNeOtcJqpoEPARMwSVWY1R1uYjcJyL3eQ57FbhERJbi+s8+raq7VHUu8C2wCFiKu88O8/F7OuOzz+DPP+HNN6FIEZ9ffsn2JTz161NcfdHV3Bd73/lPMMZkmqgXVTsi8gmuSbIqEIUbGj5dVWPOcU4EsAb3rfFv3DfRW1R1eapjagDrPQMCGgM/4JK4fECYqh4UkfzAL0BfVZ18rjhjY2N1QYj0xk9Ohlat3JxnS5ZAZvv6GmP+TUQWqmrseY5ZoqoN02xbqqoN/BtdxlzQ/WvvXrjoIvf4/Xef9zU7evIoTYY3YdeRXSy9fykl84fGF2Bjsouz3cO8Ha15N67D6wZVPSIixXBNm2elqkkicuqbZzjw8alvnp79Q4HrgDtE5CRuVNNNnkStNDDeM39OBDD6fIlZqAkPd0vbRUW51VN+/dVvE3UbY85tgYgMwI0eV+BhIHtMePPCC7Bnj98GATz969Ms37mcybdOtsTMmADyNjm7GIhX1cOeUUeNgXfPd5KqTgImpdk2NNXz14HX0zlvA66GLkurVg0GDoR77oF334XHHgt2RMbkSA8DLwBfe17/DDwfvHB8ZNEiGDLE9TeLjvb55SetncT7897n0WaP0qFGB59f3xhzdt42ay7BJUsNgc+Bj4BuqnqZf8PLmFBq1jxFFa65xk2tsWAB1K8f7IiMyT68adbMKjJ0/0pJgRYtYMMGWL3a533Nth/aTsOhDSlToAxz75lLnojArT5lTE5ytnuYt/XgSZ75yLoC76rqu7hJF7OmHbNg7YcBKUoEhg+HwoXhttvc2pvGmMARkV9EpEiq10VFJGvPRPjppzBnjl8GAagqd024iwPHDzC622hLzIwJAm+Ts4Mi8l/gdmCiZ/b/SP+F5Wdrh8DCh2HfsoAUV6oUjBgBixfDS+ccI2aM8YMSnhGaAKjqXqBU8MLJpD174OmnXc3Z7bf7/PKD5w9m0tpJvHnFm9QrVc/n1zfGnJ+3ydlNwHHcfGeJuJGbb/otKn+LGQiRhWHu3ZCSHJAir74a7r3XLXn3++8BKdIY46SIyOnx0iJShay8Gsns2XDkiBsE4ONFx7fs30Kfn/vQqWYnHmzyoE+vbYzxnlfJmSchGwUUFpHOwDFVHenXyPwpT0mIeQ92z4PV5x3X4DMDBkDVqm7ZuwMHAlasMTndc8AsEflcRD4HZgD/DXJMF65zZ9iyxQ0F97HpG6dzPPk4/dv1R3yc+BljvOft8k03AvOAG4Abgbkicr0/A/O7yt2h/NWw5Hk4uC4gRRYo4KbX2LwZHn00IEUak+N5puGJBVbjRmw+gZu6J+sqVswvl41LjCNPRB5bBcCYIPO2WfM5oImq3qmqd+Bm63/Bf2EFgAg0GQJhkTD33oCts3TJJfDf/8Inn8D48QEp0pgcTUTuwc3g/4Tn8TnwcjBjClVxiXE0LN2QiDBvZ1kyxviDt8lZmGcZpVN2Z+Dc0JWvPDR6C3ZMh/XDA1bsiy9C48bQqxckJgasWGNyqt5AE2CTqrYBGgHBX4g3xKgq8YnxNCrj+4XTjTEZ422CNVlEpohIDxHpAUwkzeSyWVb1e6B0W1jUB44kBKTIXLlc8+ahQ26CWlsc3Ri/OqaqxwBEJLeqrsItUm5S2bR/E/uO7bPkzJgQ4O2AgCdxC/c2xE1GO0xVn/ZnYAEjAs2GgybBvPsClinVrQuvvw4TJ7p50IwxfpPgmefsO+AXEfke2BrUiEJQ3LY4ABqVteTMmGDzumOBqo4FxvoxluApUA2i+sGix2HTl1DlloAU+9BD8MMPblmntm2hRo2AFGtMjqKq13qeviwivwGFgSy1Vm8gxCXGES7hNCgVUuvBG5MjnbPmTEQOisiBdB4HRSR7TQZx0SNQvDksfASO7Tj/8T4QFuYGBuTK5eaSTEoKSLHG5FiqOkNVJ6jqiWDHEmriEuOoXaI2eSPzBjsUY3K8cyZnqlpQVQul8yioqoUCFWRAhIVD84/g5EFY2DtgxVao4NYunjMH+vcPWLHGGPMPcdvirEnTmBCR9Udc+lLhulDvedj0FSRMCFix3bvDzTfDK6+4xdGNMSaQdh7eyd8H/7bBAMaECEvO0qr3DBRpCPPvgxP7Albs4MFQurRr3jxyJGDFGmMMcYmewQCWnBkTEiw5SyssEpp/DMe2Q9yTASu2aFH49FNYtQqeeSZgxRpjzOmRmtFlooMbiDEGsOQsfcVioHYfWD8CEqcGrNjLL4feveH99+HnnwNWrDEmh4tLjKNKkSoUzVs02KEYY7Dk7OwavAwFa7qlnZIOB6zY//s/qFMHevaEPXsCVqwxJgeLS4yzWjNjQoglZ2cTkReafQSH/4LFzwes2Lx54YsvYMcOuP9+Wz3AGONfh04cYu3utdbfzJgQYsnZuZRqCTUfgNXvws4/A1Zs48bQty+MGQNffhmwYo0xOdDixMUoasmZMSHEkrPzie4P+SrC3Lsh+XjAin3qKbjkEnjgAdi8OWDFGmNymNMjNW2OM2NChiVn5xNZEJoOgwMrYdmrASs2PNwtjp6cDD16QEpKwIo2xuQgcdviKJGvBOULlg92KMYYD0vOvFGuA1S9E1a8DnvjA1ZstWowcCD89hu8+27AijXG5CBxiXE0KtMIEQl2KMYYD0vOvNV4AOQuDnPuhpTALYJ5113QpQv897+wbFnAijXG5AAnkk+wbMcy629mTIjxa3ImIh1FZLWIrBORf02tKiJdRWSJiMSLyAIRudTbcwMudzGIHQx7F8HKtwJWrAgMHw6FCsFtt8HxwHV7M8Zkcyt2ruBkyknrb2ZMiPFbciYi4cBg4EqgLnCziNRNc9hUIEpVo4G7gBEZODfwKl0HFa+DpS/DgdUBK7ZUKRgxAhYvhpdeClixxphs7tTKAFZzZkxo8WfNWVNgnapuUNUTwFdA19QHqOoh1dMzeeUH1NtzgyZ2EETkg7n3gAaul36XLnDvvfD669Cvn81/Zoy/eVHzX1hEfhCRxSKyXER6ptpXRES+FZFVIrJSRC4ObPTeiUuMI39kfmoWrxnsUIwxqfgzOSsPbEn1OsGz7R9E5FoRWQVMxNWeeX1uUOQtA43fgZ2zYM0HAS168GC49VZ4/nm3zJON4DTGP7ysvX8QWKGqUUBr4G0RyeXZ9y4wWVVrA1HAyoAEnkFxiXFElYkiTKz7sTGhxJ//I9Mb+vOv+h5VHe+5gV0DnJqrwqtzAUSkl6e/2oKdO3deaKwZU/UOKNsBFj8DhzcFpkwgMhJGjoTHHnPrb956K5w4EbDijclJvKm9V6CguGGOBYA9QJKIFAJaAR8BqOoJVd0XsMi9lKIpxCfGW5OmMSHIn8lZAlAx1esKwNazHayqM4HqIlIiI+eq6jBVjVXV2JIlS2Y+am+IQNMPAYG5vQLaxhgWBm+/7Zo3v/oKrr4aDh0KWPHG5BTe1N4PAurg7k1Lgd6qmgJUA3YCn4hInIiMEJH8AYg5Q9bvWc+hE4csOTMmBPkzOZsP1BSRqp6q/u7AhNQHiEgNz7dORKQxkAvY7c25QZe/sls9IPFn+OuzgBYt4lYQ+PhjmDoV2raFQFUaGpNDeFN73wGIB8oB0cAgT61ZBNAYGKKqjYDDQLojzoNS8+9hKwMYE7r8lpypahLwEDAF199ijKouF5H7ROQ+z2HXActEJB7Xv+MmddI911+xXrCa90PJS2HhY3A0MeDF9+wJ48fD0qVw6aWwKXAtrMZkd97U3vcExnnuWeuAv4DannMTVHWu57hvccnavwSl5t8jPjGeiLAI6pWsF9ByjTHn59deoKo6SVUvUtXqqtrPs22oqg71PH9dVeuparSqXqyqs851bsiRMGg2ApKPwoIHgxLC1VfDL7/Ajh1uLU6bqNYYn/Cm9n4z0A5AREoDtYANqpoIbBGRWp7j2gErAhO29+IS46hXsh65I3IHOxRjTBo2RCezCtWChq/AlnGw+dughHDppTBzpnvesiX88UdQwjAm2/Cy5v9V4BIRWYqbs/FpVd3l2fcwMEpEluCaPP8X0DfghbhtcdakaUyIigh2ANlC7Sdg8zeu9qx0W7eaQIA1aOCSsg4d4PLL4ZtvoHPngIdhTLahqpOASWm2DU31fCvQ/iznxgOx/owvM7Yd3Mb2w9ttMIAxIcpqznwhLAKafQTH98Cix4IWRpUqMGsW1K8P11wDn34atFCMMSHs9GAAS86MCUmWnPlK0Sio+wz8NRK2/hS0MEqWhGnToE0bN2DgzTeDFooxJkSdWrYpqkxUkCMxxqTHkjNfqv88FK4Lf94J238LWhgFC8LEiXDTTW7KjT59bDUBY8wZcYlx1ChWg0K5CwU7FGNMOiw586Xw3NByHOQuDtMuh6V9ISU5KKHkygWjR8PDD7tJa3v0gJMngxKKMSbExCXGWZOmMSHMkjNfK1QLOsyHyrfA0pdgekc4uj0ooYSFwbvvwmuvweefu35ohw8HJRRjTIjYf2w/G/ZusOTMmBBmyZk/RBaAi0e6OdB2zoKfomH79KCEIgLPPQfDhsHkyW4k5549QQnFGBMC4hPjAYguEx3UOIwxZ2fJmb+IQPW7ocM8yFUYprWDZa+BBqfz1733wrffQlycmxdty5bzn2OMyX5s2SZjQp8lZ/5WpAF0WACVb4YlL8BvHeHYjqCEcu21MGUK/P23W01g5cqghGGMCaK4xDjKFChDmQJlgh2KMeYsLDkLhMgCcPHn0HQ47Pzd08w5IyihXHYZzJjhBgdceinMmROUMIwxQRK3zQYDGBPqLDkLFBGocQ+0nwsRBWFaW1jWLyjNnNHRMHs2FC0K7drBT8Gbls0YE0DHko6xYucKS86MCXGWnAVa0YbQcQFUugmWPA+/XQnHdgY8jGrV3HJPtWpBly4walTAQzDGBNiyHctI1mTrb2ZMiLPkLBgiC8Ilo6Dph7Bjhmvm3DEz4GGULg3Tp7vF0m+7DQYMANWAh2GMCZBTKwNYzZkxoc2Ss2ARgRq9oMNciMgPU9vA8v8LeDNnoUIwaRJcfz088QTceCPs3RvQEIwxARKXGEeh3IWoWrRqsEMxxpyDJWfBVjQKOi6ESjfC4mdh+lUBb+bMkwe+/hr694fvvnN90v74I6AhGGMCIC4xjugy0YSJ3fqNCWX2PzQURBaES0ZDk6FuTc6fGsGOWQENISwMnn7aJWUREdCqFbz6KiQHZ/UpY4yPJacks2T7EmvSNCYLsOQsVIhAzf9AhzkQnhemtoYVrwe8mbNpUzdR7c03w4svQtu2NmGtMdnBmt1rOHLyiCVnxmQBlpyFmqLRcOVCqHgdxD8D0zvDsV0BDaFQIfjiCxg5EhYtgqgoGD8+oCEYY3zMVgYwJuuw5CwURRaCFl9Bkw9g+1SY3Ah2Br4T2O23u+SsWjXo1g3uvx+OHg14GMYYH4jbFkfu8NzUKVEn2KEYY87DkrNQJQI174f2cyAsN/x6Gax4I+DNnDVruglr+/SBoUOhSRNYtiygIRhjfCAuMY76peoTGR4Z7FCMMedhyVmoK9YIrlwEFbtB/NNubc5dgV1zKVcuePNNmDwZdu1yCdqQITYnmjFZhaoSnxhv/c2MySIsOcsKIgtBi69dM+eeBfDzxfBLS0j4IaA1aR06wOLF0Lo1PPCAa+rcvTtgxRtjLlDCgQR2H91t/c2MySIsOcsqTjVzdt0MMe/CkS0wswtMrAfrP4bk4wEJo3RpmDgR3n7b/YyKcgupn9OBtbB1MqTYvBzGBMPpwQBWc2ZMluDX5ExEOorIahFZJyLPpLP/VhFZ4nnMFpGoVPs2ishSEYkXkQX+jDNLiSwAtR6Bq9e5udHC88Dcu2FCVTf1xol9fg8hLAwefxzmzIF8+aBNGzftRlJSqoOO7YDV78OUZvDjRTD9Svi1Fexf6ff4jDH/FLctDkFoWLphsEMxxnjBb8mZiIQDg4ErgbrAzSJSN81hfwGXqWpD4FVgWJr9bVQ1WlVj/RVnlhUWAVVuho6LoM3PULi+m3rju0qwqA8cSfB7CI0bu9GcPXq4CWs7tDvMrvmj4LdOML4cLHzE1eg1ehOaDocDq9w6osv6QcpJv8dnjHHiEuOoVaIW+XPlD3YoxhgvRPjx2k2Bdaq6AUBEvgK6AitOHaCqs1MdPweo4Md4sicRKHuFe+yJg5VvweqBsPpdqHIL1OkDRRr4rfgC+ZL4+LVfefHyLyhx7DsKrD3MYSqRv+6TUOVWKFL/zMHlr3YJ25LnYfM30PwjKBbjt9iMMU5cYhwtKrYIdhjGGC/5s1mzPJB6bvkEz7azuRv4KdVrBX4WkYUi0ssP8WU/xRpBi1GuyfOiB2HztzCpoavJ2v6b74ZXqsLu+bCgN3xXHqZfSZXIiaRUupX7vp1Bwdv+4t7B/8fhyPr/PC9vabj0a2g5Ho7vcE2e8c9AUghOnpZ8DNZ8ADO7BXwpLWN8afeR3Wzev9n6mxmThfiz5kzS2ZZudiAibXDJ2aWpNrdQ1a0iUgr4RURWqerMdM7tBfQCqFSpUuajzg4KVIGYgVD/BVg7BNa8D1PbQrFYqPsUVOgGYeEZv+7B9bBxFGz8Ag6uhbBcrjasyq1QrhOFwnPz/mVQ7CW3iPqsWfDVV27QwD9UvAZKt4a4Pq6f3JZx0GwElGqV6beeaUlHYf1wF9fRrRBRAP7+Hur+Fxq8BGE2R5TJWuIT4wFbGcCYrMSfNWcJQMVUrysAW9MeJCINgRFAV1U9PTGDqm71/NwBjMc1k/6Lqg5T1VhVjS1ZsqQPw88GcheH+s9Dl41uUfWT+2HWja6D/poPIOnI+a9xbCesGQxTLoYfasDSlyBveZdMddsOLb+FitdCeG4AIiPhf/+DX3+F/fvdWp3vvZdOpV2uIu4abX+FlCQ3ye78B+DkAV//FryTdARWvQMTqsHC3lCwBrSdCtduhap3wvJ+8HMLOLAmOPEZc4FOjdSMLhMd3ECMMV7zZ3I2H6gpIlVFJBfQHZiQ+gARqQSMA25X1TWptucXkYKnngPtAZuX/kJF5HWLql+1ElqOhdwlYcGD8H1lWPrKv9fuTDoCG79063qOLwcLHoLkwxD9upvK4/LfoPrdLsE6i7ZtYckSaN8eeveGzp1h9ep0DizTDq5aCrUeg7VDYWJ9+HuST9/+OZ08BCvedKNdFz0OhetCu+lw+Qwo0xYiC0Lzj+HSb+HQevipEawbbjPw5gBejDYvLCI/iMhiEVkuIj3T7A8XkTgR+TFwUf9bXGIcFQpVoES+EsEMwxiTAX5LzlQ1CXgImAKsBMao6nIRuU9E7vMc9iJQHPggzZQZpYFZIrIYmAdMVNXJ/oo1xwgLdysNtP8TLp8JJS6GpS/D95Vg/kOw5Tv4804YVxpm3wL7FkPtx+HKxdBpiWsSzV/xfKWcVqIETJjgas5mzIC6deGuu2DTpjQHRuSHmAHQfrZLhmZcBbNv9++C7ycPwvL+LimLfwqKRMHlv0O7qVD6sn8fX+k69zsoeQnM6wW/XxvwBelN4Hg52vxBYIWqRgGtgbc9X0RP6Y279wVV3LY4629mTBbjzz5nqOokYFKabUNTPb8HuCed8zYAaXsqGV8RgVIt3WP/CjfCc/0wWDsYIgtD5e6uH1mpViCZy99F4OGH4aab4P/+zy379MUX8J//wHPPQZkyqQ4u0dxNDbL8f+6xbQrEvg+VbnQX8oUT+2HNIFg1AE7sgbJXur55JS8+/7n5ykObKW4kbPwzMKkBNP8EynX0TWwmlJx3tDmuD21BERGgALAHSPIcXwG4CugHPB7AuP/hyMkjrN69mhvq3hCsEIwxF8BWCMjpCtd1zXZd/nLzpXVLhGbDXYf9TCZmqZUqBe+8A2vXunnRhgyBatXgmWdgz55UB4bnhoavQMeFkL8y/NEdZl4DR/7VXTFjTuxzTbjfV3FTeZS4BDrMgzaTvEvMTpEwqP0YdJgPuUu4yXUXPBKaI05NZngz2nwQUAfXl3Yp0Fv19HpqA4GngMCtr5aOJduXkKIpNhjAmCzGkjPj5Cvv5koLz+PXYipWhGHDYNUqtzbnG29A1apuEtuDB1MdWLSha35t9CYk/gwT68K6ERnv63V8Dyx50dO/7mXXZNlxIbT+AYo3ufA3UrQhdJwPtXq70bBTmsDexRd+PRNqvBlt3gGIB8oB0cAgESkkIp2BHaq68LyFiPQSkQUismDnzp2ZDPnf4rbZsk3GZEWWnJmgqFHDNW8uWeIGD7z4oqtJGzAAjp6qhAqLcJPodloKRaNh3r0wrZ2b0uN8ju2C+GddUrbsVShzBVwZB62+g2KNffMmwvO4KUtaT4bju2FKU1j5dkAXozd+481o857AOHXW4VY8qQ20ALqIyEbgK6CtiHyRXiH+Hm0elxhH0TxFqVTYphkyJiux5MwEVf36MH48zJ0LjRrBE09AzZrw4Ydw8tQKTwVrQLtp0PRD2L3A9fVaOSD9hdSP7YC4p2FCFVjRH8p1ch35W37rEjx/KNfBJZDlOrm526ZdEZDls4xfnXe0ObAZaAcgIqWBWsAGVf2vqlZQ1Sqe86ap6m2BC/2MuMQ4GpVthPiqz6YxJiAsOTMhoWlT+Pln+O03qFwZ7rsPateGzz+H5GRcX68avaDzCijdFuKegF8ugX2eGVaOJro1Rb+vCqvegvJd4aplbkUCPy5fdVqeEtBynFtDdNcctzLD5m/9X67xCy9Hm78KXCIiS4GpwNOqGjJDeE8mn2Tp9qXWpGlMFmTJmQkprVu7lQUmToRCheCOO6BhQxg3ztPdLF8FuOwHuGQ0HNoAkxvD79e5KTFWvwMVr4OrVrhlrAqnnfnAz0Sgxj1wZTwUqAGzboA5PYM3sa7JFFWdpKoXqWp1Ve3n2Tb01IhzVd2qqu1VtYGq1lfVfzVdqup0Ve0c6NgBVu1axfHk45acGZMFWXJmQo4IdOoECxfCmDGQkgLXXedq16ZMAUWgys0uCat4Pfz9o5v+o/NquGQkFKoV3DdQqCa0/wPqPQ9/jYRJ0bBzdnBjMjnOqZUBbKSmMVmPJWcmZIWFwQ03wNKl8MknsHMndOx4pnaNPCWhxWi46Zibb6xgjWCHfEZYJES9Cu1mAAq/toQlL7mlqozrL3jyoJsi5cAa2LMIdsx0q0Mc2hDs6LKFuG1x5I3IS63iQf6yYozJML9OQmuML0REuLnRbr4ZRoyA116Dli1dovbaaxATE8KdnUtd6po5FzwMy/q6iXUv+SK0EsmMOLEPDv0FJ/e5pa+SUj3Svj7XtuRzrOvaeCDU7h2gN5R9xSXG0bB0Q8LDwoMdijEmgyw5M1lG7tzw4IPQsycMHgz9+0NsrGvyfPZZaOyjGTJ8Lldh19xa/iqYdx/8FA0x70G1nr5b+cBXVOFYopuu5NA6z0/P4+A6t6rCuYTlhsgCEJHqEVkQ8pRK9TrN/rTbClQNzHvNxlSV+MR4bq5/c7BDMcZcAEvOTJaTLx88+ST06uVWHXjnHRg7Fi6/3K040LZt6OU8AFS+ya1M8OcdMPduSPgOSreBXEUhsohbSD5XkTOvIwv6dJWG01KS4Mhml2wdWn8mATu4zjUppq7VkjDIV9nV9FW6EQpWhwLVIFexdBKr/K451wTdxn0b2X98v/U3MyaLsuTMZFmFC8PLL8Njj7l50d55xyVoMTHw9NNuBYLwUGvRyV/RLa6+8m1Y+iL8/cPZj5Uwt9Zpeolbes9zFTnzOiK/m2vtX8nXeji8CTRV37fwPC7hKlDDTdZbsDoUqO4SsvyVLeHKgk4PBrCRmsZkSZacmSyvcGF46ino3dvNi/bGG3DjjW4Vgj594M47IY9/V6XKGAmDuk9C7cch6YDrx3Vin+vHdWLvuV8fWH3medJh78uMLOKSrWKxrgbvVPJVoDrkLeufGjoTNHHb4giXcBqUDsAcf8YYn7PkzGQbuXPDPfe4PmnffQevv+4ms33pJZe43X8/FCkS7ChTCQv31HgVvbDzk0/Ayf3/TuRO7nMjIfOWP1MLlruY7+I2IS8uMY46JeuQJyKUvpUYY7xlyZnJdsLD3SCBbt1g+nSXpD37LPzf/8F//uOaQcuVC3aUPhCeC8JLuilFjEklLjGOdlXbBTsMY8wFsrYMk22JQJs2MHkyxMVB585uYfUqVeDuu2HVqmBHaIzv7Ti8g60Ht1p/M2OyMEvOTI4QHQ2jR8O6dW6U5+jRULcuXHstzJkT7OiM8Z24bbYygDFZnSVnJkepWhUGDYLNm+H552HGDLj4YrfqwE8/edbvNCYLOzVSM7pMdHADMcZcMEvOTI5UsiT07euStAEDYP16t55ndDSMGgVJtsqSyaLiEuOoUqQKRfIUCXYoxpgLZMmZydEKFHADBNavh08/dUnZbbe5aTjefx+OnGOVIWNCUdy2OOtvZkwWZ8mZMUCuXG4+tKVLYcIEqFABHnkEKlVyzZ/btgU7QmPO7+Dxg6zds9aSM2OyOEvOjEklLAyuvhpmzXKPSy+F//0PKleGO+5woz6NCVWLty8GbDCAMVmdJWfGnEWLFm4y2zVr3GS248a5xdVbt4bvv4fk5GBHaMw/nR6paTVnxmRplpwZcx41asB770FCArz5Jvz1F1xzDdSq5fqlHToU7AiNceIS4yiZryTlCmaHWZaNybn8mpyJSEcRWS0i60TkmXT23yoiSzyP2SIS5e25xgRakSJurc716+Hrr92Iz0cecf3TnnzSjfw0JpjiEuNoVLYRIhLsUIwxmeC35ExEwoHBwJVAXeBmEamb5rC/gMtUtSHwKjAsA+caExQREW5h9T//dI8OHeCdd6BaNbjpJpvU1gTHieQTLN+x3Jo0jckG/Flz1hRYp6obVPUE8BXQNfUBqjpbVfd6Xs4BKnh7rjGhoHlzV4u2YYObkmPKFDep7cUXw5gxNl+aCZzlO5ZzMuWkJWfGZAP+TM7KA1tSvU7wbDubu4GfLvBcY4KqUiXXHy0hwfVD27XL1aJVrw5vvQX79gU7QpPdnVoZwEZqGpP1+TM5S6/TQ7qL44hIG1xy9vQFnNtLRBaIyIKdO3deUKDG+EqBAvDQQ25R9e+/d02dTz55Zt60deuCHaHJruK2xVEgVwFqFKsR7FCMMZnkz+QsAaiY6nUFYGvag0SkITAC6KqquzNyLoCqDlPVWFWNLVmypE8CNyazwsOhSxf47TdYtAiuuw6GDoWLLoKuXWH6dFvH0/hWXGIcUaWjCBMbhG9MVufP/8XzgZoiUlVEcgHdgQmpDxCRSsA44HZVXZORc43JKho1gs8+g02b4Lnn4I8/oE0biIlxtWuWpJnMStEUFm9fbP3NjMkm/JacqWoS8BAwBVgJjFHV5SJyn4jc5znsRaA48IGIxIvIgnOd669YjQmEsmXh1VdhyxYYNszNj3bNNW7wwLRpwY7OZGXr9qzj0IlD1t/MmGzCr/XfqjpJVS9S1eqq2s+zbaiqDvU8v0dVi6pqtOcRe65zjckO8uaFe++FFStgxAj4+29o1w4uvxzmzQt2dCYrspUBjMlerHOCMUESEQF33w1r17p50hYvhmbN4NprYdmyYEdnspL4xHgiwyKpV6pesEMxxviAJWfGBFmePPDoo26utL59XRNnw4ZuofUNG4IdXc7lxQonhUXkBxFZLCLLRaSnZ3tFEflNRFZ6tvf2d6xxiXHUK1WPXOG5/F2UMSYALDkzJkQULAgvvOASsj594JtvoHZtePBB2LYt2NHlLF6uUvIgsEJVo4DWwNueAUxJwBOqWgdoDjzozxVOVNUt22RNmsZkG5acGRNiiheHN95wa3jefbcbPFC9OjzzDOzZE+zocgxvVilRoKC4hSwLAHuAJFXdpqqLAFT1IG5Qk98m0d52aBs7Du+w5MyYbMSSM2NCVLlyMGSIm9D2uutcwlatGvTr50Z6Gr/yZpWSQUAd3ByMS4HeqpqS+gARqQI0Aub6K9DTgwFspKYx2YYlZ8aEuOrV4fPP3YCB1q3h+efdtvfeg+PHgx1dtuXNKiUdgHigHBANDBKRQqcvIFIAGAs8qqoH0i3EByucxCXGIQhRpaMu6HxjTOix5MyYLKJBA/juO/jzT6hXD3r3disOfPyxLbDuB96sUtITGKfOOuAvoDaAiETiErNRqjrubIX4YoWTuMQ4ahSrQcHcBS/ofGNM6LHkzJgspnlzN6Lzl1+gdGnXL61BA/j2W0hJOf/5xiverFKyGWgHICKlgVrABk8ftI+Alao6wN+Bxm2LI7pMtL+LMcYEkCVnxmRRl18Oc+fC+PEQFgY33ABNmsCUKbYkVGZ5ucLJq8AlIrIUmAo8raq7gBbA7UBbz8on8SLSyR9x7ju2j7/2/WWDAYzJZiKCHYAx5sKJuCWgrr4aRo+GF1+Ejh2hZUvo1s3VsjVqBLlzBzvSrEdVJwGT0mwbmur5VqB9OufNIv0+az4XnxgP2GAAY7IbqzkzJhsID4fbb4fVq2HwYEhIgMcec+t2FirkVh7o3Ru+/BL++stq1rILW7bJmOzJas6MyUZy5YIHHnCPrVtds+ecOe4xfLgb4QlQsqSrVTv1aNLETYJrspa4xDjKFihL6QKlgx2KMcaHLDkzJpsqV86t03ntte51UpJbs/NUsjZnDvzwg9sn4kaAnkrWmjWDOnVcjZwJXXGJcdakaUw2ZMmZMTlERARER7vHfZ4u7Xv3wrx5LlGbOxfGjoURI9y+ggWhadMzyVqzZlCqVLCiN2kdPXmUlTtX0rVW2oULjDFZnSVnxuRgRYtChw7uAa4v2tq1Z5K1OXOgf39ITnb7q1Vzydrtt7tzJCDd3k16lu1YRrImW38zY7IhS86MMaeJuIltL7oI7rjDbTtyBBYuPJOs/fKLGxlat64bdHDbbZAnT3DjzoniEm3ZJmOyKxutaYw5p3z53NQcffq4iW4TEmDkSDf44N57oVIleOUV2LEj2JHmLHHb4iicuzBVi1QNdijGGB+z5MwYkyG5crlmzUWL3EoFTZvCyy+7JK1XL1i5MtgR5gxxiW5lALG2ZWOyHUvOjDEXRATatIEff3QJWY8eboH2unWhUyeYOtXmU/OX5JRklmxfYv3NjMmmLDkzxmRa7dowdChs2QJ9+7o+apdf7kaGfvYZHD8e7Aizl9W7V3M06aj1NzMmm7LkzBjjMyVKwAsvwKZN8NFHbpRnjx5QtSr873+we3ewI8webGUAY7I3S86MMT6XJw/cdRcsXeoWYm/QAJ57DipWhAcfdNN1mAsXlxhH7vDc1C5RO9ihGGP8wJIzY4zfiED79i5BW7oUbr7ZTXJbqxZ07QozZli/tAsRlxhHg9INiAyPDHYoxhg/sOTMGBMQ9eu7ps7Nm+H55+GPP6B1a7eu5+jRcPJksCPMGlSV+MR4a9I0Jhvza3ImIh1FZLWIrBORZ9LZX1tE/hSR4yLSJ82+jSKyVETiRWSBP+M0xgRO6dJu0MCWLfDhh3DoENx6q1t94M03Yd++YEcY2rYc2MKeo3ssOTMmG/NbciYi4cBg4EqgLnCziNRNc9ge4BHgrbNcpo2qRqtqrL/iNMYER968bl60FSvcdBwXXQRPPQVDhgQ7stB2ejCAjdQ0Jtvy5/JNTYF1qroBQES+AroCK04doKo7gB0icpUf4zDGhLCwMLjqKveIj3eT2Zqza1+9PXPunkNUmahgh2KM8RN/NmuWB7akep3g2eYtBX4WkYUi0sunkRljQlJ0NBQrFuwoQlveyLw0q9CMPBG2oKkx2ZU/a87SW1MkI+OyWqjqVhEpBfwiIqtUdea/CnGJWy+ASvaV2xhjjDFZnD9rzhKAiqleVwC2enuyqm71/NwBjMc1k6Z33DBVjVXV2JIlS2YiXGOMMcaY4PNncjYfqCkiVUUkF9AdmODNiSKSX0QKnnoOtAeW+S1SY4wxxpgQ4bdmTVVNEpGHgClAOPCxqi4Xkfs8+4eKSBlgAVAISBGRR3EjO0sA40XkVIyjVXWyv2I1xhhjjAkV/uxzhqpOAial2TY01fNEXHNnWgcAG4pkjDHGmBzHVggwxhhjjAkhlpwZY4wxxoQQS86MMcYYY0KIJWfGGGOMMSHEkjNjjDHGmBAiqhmZtD+0ichOYFOw48BNBbIr2EFcAIs78LJq7KESd2VVzRazT4fQ/QtC5983oyzuwLK4My/de1i2Ss5ChYgsUNXYYMeRURZ34GXV2LNq3MY7WfXf1+IOLIvbf6xZ0xhjjDEmhFhyZowxxhgTQiw5849hwQ7gAlncgZdVY8+qcRvvZNV/X4s7sCxuP7E+Z8YYY4wxIcRqzowxxhhjQoglZz4kIhVF5DcRWSkiy0Wkd7BjyggRCReROBH5MdixeEtEiojItyKyyvN7vzjYMXlDRB7zfEaWiciXIpIn2DGlR0Q+FpEdIrIs1bZiIvKLiKz1/CwazBiNb9j9K/Ds/uV/WfUeZsmZbyUBT6hqHaA58KCI1A1yTBnRG1gZ7CAy6F1gsqrWBqLIAvGLSHngESBWVesD4UD34EZ1Vp8CHdNsewaYqqo1game1ybrs/tX4Nn9y/8+JQvewyw58yFV3aaqizzPD+L+o5UPblTeEZEKwFXAiGDH4i0RKQS0Aj4CUNUTqrovqEF5LwLIKyIRQD5ga5DjSZeqzgT2pNncFfjM8/wz4JpAxmT8w+5fgWX3r8DIqvcwS878RESqAI2AuUEOxVsDgaeAlCDHkRHVgJ3AJ57mjBEikj/YQZ2Pqv4NvAVsBrYB+1X15+BGlSGlVXUbuD/oQKkgx2N8zO5fAWH3r+AJ+XuYJWd+ICIFgLHAo6p6INjxnI+IdAZ2qOrCYMeSQRFAY2CIqjYCDhOC1dNpefo3dAWqAuWA/CJyW3CjMsax+1fA2P3LnJUlZz4mIpG4G9soVR0X7Hi81ALoIiIbga+AtiLyRXBD8koCkKCqp77df4u72YW6y4G/VHWnqp4ExgGXBDmmjNguImUBPD93BDke4yN2/woou38FT8jfwyw58yEREVz/gZWqOiDY8XhLVf+rqhVUtQquY+c0VQ35b0KqmghsEZFank3tgBVBDMlbm4HmIpLP85lpRxboCJzKBOBOz/M7ge+DGIvxEbt/BZbdv4Iq5O9hEcEOIJtpAdwOLBWReM+2Z1V1UvBCyvYeBkaJSC5gA9AzyPGcl6rOFZFvgUW4EXJxhOiM1SLyJdAaKCEiCcBLQH9gjIjcjbtR3xC8CI0P2f0r8Oz+5WdZ9R5mKwQYY4wxxoQQa9Y0xhhjjAkhlpwZY4wxxoQQS86MMcYYY0KIJWfGGGOMMSHEkjNjjDHGmBBiyZnJtkSktYj8GOw4jDHmQtg9LOey5MwYY4wxJoRYcmaCTkRuE5F5IhIvIh+KSLiIHBKRt0VkkYhMFZGSnmOjRWSOiCwRkfGedd4QkRoi8quILPacU91z+QIi8q2IrBKRUZ4ZrRGR/iKywnOdt4L01o0x2YDdw4yvWXJmgkpE6gA3AS1UNRpIBm4F8gOLVLUxMAM3qzPASOBpVW0ILE21fRQwWFWjcOu8bfNsbwQ8CtQFqgEtRKQYcC1Qz3Od1/z5Ho0x2Zfdw4w/WHJmgq0dEAPM9ywZ0w53A0oBvvYc8wVwqYgUBoqo6gzP9s+AViJSECivquMBVPWYqh7xHDNPVRNUNQWIB6oAB4BjwAgR6QacOtYYYzLK7mHG5yw5M8EmwGeqGu151FLVl9M57lzrjMk59h1P9TwZiFDVJKApMBa4BpicsZCNMeY0u4cZn7PkzATbVOB6ESkFICLFRKQy7rN5veeYW4BZqrof2CsiLT3bbwdmqOoBIEFErvFcI7eI5DtbgSJSACjsWdD5USDa5+/KGJNT2D3M+FxEsAMwOZuqrhCR54GfRSQMOAk8CBwG6onIQmA/rk8HwJ3AUM+NawPQ07P9duBDEenrucYN5yi2IPC9iOTBfWN9zMdvyxiTQ9g9zPiDqJ6rptWY4BCRQ6paINhxGGPMhbB7mMkMa9Y0xhhjjAkhVnNmjDHGGBNCrObMGGOMMSaEWHJmjDHGGBNCLDkzxhhjjAkhlpwZY4wxxoQQS86MMcYYY0KIJWfGGGOMMSHk/wGJgjlwk2YhygAAAABJRU5ErkJggg=="/> + +#### 모델 평가와 예측 + + + +```python +model.evaluate(X_val, y_val) +``` + +<pre> +375/375 [==============================] - 1s 3ms/step - loss: 0.2228 - acc: 0.9231 +</pre> +<pre> +[0.22275471687316895, 0.9230833053588867] +</pre> +evaluate 메소드를 사용해서 이 모델을 검증 데이터로 평가해보면 + +손실값은 약 0.22 정도 나오고, 정확도는 0.92정도가 나옵니다. + + + +<br/> + + + +이미지 하나를 28x28로 reshape 해서 이미지를 출력해보면 아래와 같은 가방 이미지가 출력됩니다. + +그리고 predict 메소드를 사용해서 각 10개의 카테고리에 포함될 10개의 확률을 확인 할 수 있습니다. + +9번째 확률값이 제일 높은 1이므로, 이 이미지는 카테고리 9에 해당하는 데이터라고 예측 됨을 확인 할 수 있습니다. + + + +```python +plt.imshow(X_val[0].reshape(28, 28), cmap='gray') +plt.show() + +preds = model.predict(X_val[0:1]) +print(preds) +``` + +<img src="data:image/png;base64,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"/> + +<pre> +[[2.5790641e-21 1.0722967e-27 1.9750217e-24 1.0985332e-21 5.8698841e-18 + 2.0373612e-21 2.8928411e-18 3.8926373e-19 1.0000000e+00 3.8500301e-20]] +</pre> +이번에는 테스트 데이터를 evaluate 메소드를 통해 예측을 해보면 결과는 검증 셋보다 + +약간 더 높은 0.25 정도의 손실값과 약 0.91의 정확도가 나오는 것을 확인 할 수 있습니다. + + + +```python +model.evaluate(X_test, y_test) +``` + +<pre> +313/313 [==============================] - 1s 4ms/step - loss: 0.2519 - acc: 0.9155 +</pre> +<pre> +[0.2519080638885498, 0.9154999852180481] +</pre> \ No newline at end of file diff --git "a/_posts/2022-02-07-IMDB \352\270\215\354\240\225 \354\227\254\353\266\200 \354\230\210\354\270\241(RNN, Embedding).md" "b/_posts/2022-02-07-IMDB \352\270\215\354\240\225 \354\227\254\353\266\200 \354\230\210\354\270\241(RNN, Embedding).md" new file mode 100644 index 000000000000..4b019e78d05d --- /dev/null +++ "b/_posts/2022-02-07-IMDB \352\270\215\354\240\225 \354\227\254\353\266\200 \354\230\210\354\270\241(RNN, Embedding).md" @@ -0,0 +1,627 @@ +--- +layout: single +title: "IMDB 긍정 여부 분류 (RNN, Embedding)" +categories: 딥러닝 +tag: [python, 파이썬, keras, 딥러닝, imdb, RNN, 시퀀스 패딩, Embedding, 예측] +toc: true +toc_sticky: true + +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 8px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + + .output_prompt { + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; + -webkit-overflow-scrolling: touch; + padding: 0.8rem; + margin-top: 0; + margin-bottom: 15px; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: $code-text-color; + border: solid 1px $border-color; + border-radius: 0.3rem; + word-break: normal; + white-space: pre; + } + + .dataframe tbody tr th:only-of-type { + vertical-align: middle; + } + + .dataframe tbody tr th { + vertical-align: top; + } + + .dataframe thead th { + text-align: center !important; + padding: 8px; + } + + .page__content p { + margin: 0 0 0px !important; + } + + .page__content p > strong { + font-size: 0.8rem !important; + } + + </style> +</head> + + +#### IMDB 영화 리뷰 긍정 여부 예측 모델 학습 + +총 25000개의 샘플이 존재하며, 각 샘플은 영화 리뷰 한 건을 의미합니다. + +데이터셋은 이미 정수로 인코딩되어 있고, 정수값은 단어의 빈도수를 나타냅니다. + + + +<br/> + + + ++ imdb.load_data()의 인자로 num_words : 단어의 등장 빈도 순위로 몇 등까지 사용할 것인지를 의미합니다. + + num_words를 10000으로 설정하여 단어의 등장 빈도 순위가 10000을 넘는 단어는 보이지 않게 해서 데이터셋을 + + 좀 더 간단하게 표현하기 위해 사용합니다. + + 즉, 이 데이터에 사용할 단어 사전에 10000개의 단어를 사용합니다. + + + +```python +from tensorflow.keras.datasets import imdb +import matplotlib.pyplot as plt +import numpy as np + +num_word = 500 + +(X_train, y_train), (X_test, y_test) = imdb.load_data(num_words=num_word) + +X_train.shape, y_train.shape +``` + +<pre> +((25000,), (25000,)) +</pre> +X_train의 샘플을 확인해보면 각 단어의 빈도 순위가 정수형으로 나타나 있는걸 확인할 수 있습니다. + + + +```python +print(X_train[0]) +``` + +<pre> +[1, 14, 22, 16, 43, 2, 2, 2, 2, 65, 458, 2, 66, 2, 4, 173, 36, 256, 5, 25, 100, 43, 2, 112, 50, 2, 2, 9, 35, 480, 284, 5, 150, 4, 172, 112, 167, 2, 336, 385, 39, 4, 172, 2, 2, 17, 2, 38, 13, 447, 4, 192, 50, 16, 6, 147, 2, 19, 14, 22, 4, 2, 2, 469, 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, 386, 12, 8, 316, 8, 106, 5, 4, 2, 2, 16, 480, 66, 2, 33, 4, 130, 12, 16, 38, 2, 5, 25, 124, 51, 36, 135, 48, 25, 2, 33, 6, 22, 12, 215, 28, 77, 52, 5, 14, 407, 16, 82, 2, 8, 4, 107, 117, 2, 15, 256, 4, 2, 7, 2, 5, 2, 36, 71, 43, 2, 476, 26, 400, 317, 46, 7, 4, 2, 2, 13, 104, 88, 4, 381, 15, 297, 98, 32, 2, 56, 26, 141, 6, 194, 2, 18, 4, 226, 22, 21, 134, 476, 26, 480, 5, 144, 30, 2, 18, 51, 36, 28, 224, 92, 25, 104, 4, 226, 65, 16, 38, 2, 88, 12, 16, 283, 5, 16, 2, 113, 103, 32, 15, 16, 2, 19, 178, 32] +</pre> +이 데이터 셋은 리뷰의 긍정여부를 나타내기 때문에 y값은 0과 1 두개의 값만 가지는 것을 확인할 수 있습니다. + + + +```python +print(np.unique(y_train)) +``` + +<pre> +[0 1] +</pre> +이 데이터는 토큰화와 정수 인코딩이라는 텍스트 전처리가 완료된 상태입니다. + +다음에 시퀀스 패딩을 사용하기 위해 적절한 리뷰의 길이 즉, 토큰의 길이와 그에 따른 빈도 수를 확인해봅니다. + + + +<br/> + + + +대체적으로 1000이하의 길이를 가지며, 100 ~ 500정도의 길이를 가진 데이터가 많은 것을 확인할 수 있습니다. + + + +```python +from sklearn.model_selection import train_test_split + +X_train, X_val, y_train, y_val = train_test_split(X_train, y_train, + test_size=0.2, random_state=42) + +length = np.array([len(x) for x in X_train]) +print(f"X_train의 평균 : {np.mean(length)}\nX_train의 중간값 : {np.median(length)}") + +plt.hist(length) +plt.xlabel('length') +plt.ylabel('frequency') +plt.show() +``` + +<pre> +X_train의 평균 : 239.00925 +X_train의 중간값 : 178.0 +</pre> +<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAZIAAAEGCAYAAABPdROvAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8QVMy6AAAACXBIWXMAAAsTAAALEwEAmpwYAAAWR0lEQVR4nO3df7DldX3f8ecru5GASgRZGLJLs2u6xoCTqmwoamM7wREUdamVuk4s20hnJwyp2jZtlthRk5YpmmhS0ojBiCz+AuqPshNDlK7aTFsCXhDll5QVEFY2sP5kTVriknf/+H6uOdy99+7Z/d5zzj3Z52PmzPme9/l+z/d9vmd3X/v9napCkqRD9SOTbkCSNN0MEklSLwaJJKkXg0SS1ItBIknqZeWkGxi34447rtauXTvpNiRpqtxyyy3frKpV87132AXJ2rVrmZmZmXQbkjRVknx9offctCVJ6sUgkST1YpBIknoxSCRJvRgkkqReDBJJUi8GiSSpF4NEktSLQSJJ6uWwO7O9j7VbPz2xeT9wydkTm7ckLcY1EklSLwaJJKkXg0SS1ItBIknqxSCRJPVikEiSejFIJEm9GCSSpF4MEklSLwaJJKkXg0SS1ItBIknqxSCRJPVikEiSejFIJEm9GCSSpF4MEklSLyMLkiRXJHk0yR0DtWOT3JDk3vZ8zMB7FyXZmeSeJGcO1E9Ncnt779IkafUjklzT6jclWTuq7yJJWtgo10iuBM6aU9sK7Kiq9cCO9pokJwObgFPaNO9NsqJNcxmwBVjfHrOfeT7wnar6u8DvAO8c2TeRJC1oZEFSVX8KfHtOeSOwrQ1vA84ZqF9dVY9X1f3ATuC0JCcCR1fVjVVVwFVzppn9rI8DZ8yurUiSxmfc+0hOqKrdAO35+FZfDTw0MN6uVlvdhufWnzRNVe0Dvgc8c76ZJtmSZCbJzJ49e5boq0iSYPnsbJ9vTaIWqS82zf7FqsurakNVbVi1atUhtihJms+4g+SRtrmK9vxoq+8CThoYbw3wcKuvmaf+pGmSrAR+nP03pUmSRmzcQbId2NyGNwPXDdQ3tSOx1tHtVL+5bf7am+T0tv/jvDnTzH7Wa4HPtf0okqQxWjmqD07yMeAfAccl2QW8HbgEuDbJ+cCDwLkAVXVnkmuBu4B9wIVV9UT7qAvojgA7Eri+PQA+AHwoyU66NZFNo/oukqSFjSxIqur1C7x1xgLjXwxcPE99BnjuPPX/RwsiSdLkLJed7ZKkKWWQSJJ6MUgkSb0YJJKkXgwSSVIvBokkqReDRJLUi0EiSerFIJEk9WKQSJJ6MUgkSb0YJJKkXgwSSVIvBokkqReDRJLUi0EiSerFIJEk9WKQSJJ6MUgkSb0YJJKkXgwSSVIvBokkqReDRJLUi0EiSerFIJEk9WKQSJJ6MUgkSb0YJJKkXiYSJEn+VZI7k9yR5GNJfizJsUluSHJvez5mYPyLkuxMck+SMwfqpya5vb13aZJM4vtI0uFs7EGSZDXwJmBDVT0XWAFsArYCO6pqPbCjvSbJye39U4CzgPcmWdE+7jJgC7C+Pc4a41eRJDG5TVsrgSOTrASOAh4GNgLb2vvbgHPa8Ebg6qp6vKruB3YCpyU5ETi6qm6sqgKuGphGkjQmYw+SqvoG8NvAg8Bu4HtV9VnghKra3cbZDRzfJlkNPDTwEbtabXUbnlvfT5ItSWaSzOzZs2cpv44kHfYmsWnrGLq1jHXATwBPTfKGxSaZp1aL1PcvVl1eVRuqasOqVasOtmVJ0iImsWnrpcD9VbWnqn4AfBJ4EfBI21xFe360jb8LOGlg+jV0m8J2teG5dUnSGE0iSB4ETk9yVDvK6gzgbmA7sLmNsxm4rg1vBzYlOSLJOrqd6je3zV97k5zePue8gWkkSWOyctwzrKqbknwcuBXYB3wJuBx4GnBtkvPpwubcNv6dSa4F7mrjX1hVT7SPuwC4EjgSuL49JEljNPYgAaiqtwNvn1N+nG7tZL7xLwYunqc+Azx3yRuUJA3NM9slSb0YJJKkXgwSSVIvBokkqReDRJLUi0EiSerFIJEk9WKQSJJ6MUgkSb0YJJKkXgwSSVIvBokkqZcDBkm7s+CF7YZUkiQ9yTBrJJvo7mT4xSRXJzmz3f9DkqQDB0lV7ayqtwLPBj4KXAE8mOQ3khw76gYlScvbUPtIkvws8G7gt4BPAK8FHgM+N7rWJEnT4IA3tkpyC/Bd4APA1qp6vL11U5IXj7A3SdIUGOYOiedW1X3zvVFVr1nifiRJU2aYTVv/IskzZl8kOSbJfxxdS5KkaTJMkLy8qr47+6KqvgO8YmQdSZKmyjBBsiLJEbMvkhwJHLHI+JKkw8gw+0g+DOxI8kGggDcC20balSRpahwwSKrqXUluB84AAvyHqvrMyDuTJE2FYdZIqKrrgetH3IskaQoNc62t1yS5N8n3kjyWZG+Sx8bRnCRp+RtmjeRdwKuq6u5RNyNJmj7DHLX1iCEiSVrIMGskM0muAf4bMHt5FKrqk6NqSpI0PYZZIzka+EvgZcCr2uOVfWaa5BlJPp7kq0nuTvLCJMcmuaHtj7lh8P4nSS5KsjPJPUnOHKifmuT29t6lXt5eksZvmMN/f2kE8/3PwJ9U1WuTPAU4Cvh1YEdVXZJkK7AV+LUkJ9PdE+UUuvui/Pckz66qJ4DLgC3AnwF/DJyFR5dJ0lgNc9TWs5PsSHJHe/2zSf79oc4wydHAS+iuJkxV/VW7BMtG/uZEx23AOW14I3B1VT1eVfcDO4HTkpwIHF1VN1ZVAVcNTCNJGpNhNm29H7gI+AFAVX2Fbg3hUD0L2AN8MMmXkvxhkqcCJ1TV7jaP3cDxbfzVwEMD0+9qtdVteG59P0m2tFsGz+zZs6dH65KkuYYJkqOq6uY5tX095rkSeAFwWVU9H/gLus1YC5lvv0ctUt+/WHV5VW2oqg2rVq062H4lSYsYJki+meSnaP9IJ3ktsLvHPHcBu6rqpvb643TB8kjbXEV7fnRg/JMGpl8DPNzqa+apS5LGaJgguRD4A+A5Sb4BvAW44FBnWFV/DjyU5Kdb6QzgLmA7sLnVNgPXteHtwKYkRyRZB6wHbm6bv/YmOb0drXXewDSSpDEZ5qit+4CXtv0YP1JVe5dgvv8S+Eg7Yus+4JfoQu3aJOcDDwLntvnfmeRaurDZB1zYjtiCLtCuBI6kO1rLI7YkaczSHfC0yAjJ2+arV9VvjqSjEduwYUPNzMwc0rRrt356ibtZ/h645OxJtyBpGUhyS1VtmO+9Yc5s/4uB4R+jOxnRS6ZIkoDhNm29e/B1kt+m228hSdJQO9vnOoruXBBJkg68RtLujji7I2UFsAqYyv0jkqSlN8w+ksELNO6ju6x8nxMSJUl/iwwTJHMP9z168CK7VfXtJe1IkjRVhgmSW+nOLP8O3WVJnkF3ngd0m7zcXyJJh7Fhdrb/Cd2tdo+rqmfSber6ZFWtqypDRJIOc8MEyc9V1R/Pvqiq64F/OLqWJEnTZJhNW99s9x/5MN2mrDcA3xppV5KkqTHMGsnr6Q75/VR7rGo1SZKGOrP928Cbkzytqr4/hp4kSVNkmFvtvijJXXRX3yXJ30vy3pF3JkmaCsNs2vod4EzafpGq+jLdPdclSRruWltV9dCc0hPzjihJOuwMc9TWQ0leBFS7EdWb8DLykqRmmDWSX6a73e5quvukP6+9liRp8TWSJCuA362qXxxTP5KkKbPoGkm7N/qqtklLkqT9DLOP5AHgfyXZzsBtd6vqPaNqSpI0PRZcI0nyoTb4OuCP2rhPH3hIkrToGsmpSX6S7pLxvzemfiRJU2axIHkf3SXk1wEzA/XgfUgkSc2Cm7aq6tKq+hngg1X1rIGH9yGRJP3QAc8jqaoLxtGIJGk6DXWJFEmSFmKQSJJ6MUgkSb1MLEiSrEjypSR/1F4fm+SGJPe252MGxr0oyc4k9yQ5c6B+apLb23uXJskkvoskHc4muUbyZp58FeGtwI6qWg/saK9JcjKwCTgFOAt4b7sGGMBlwBZgfXucNZ7WJUmzJhIkSdYAZwN/OFDeCGxrw9uAcwbqV1fV41V1P7ATOC3JicDRVXVjVRVw1cA0kqQxmdQaye8C/w7464HaCVW1G6A9H9/qq4HBG2vtarXZy9rPrUuSxmjsQZLklcCjVXXLsJPMU6tF6vPNc0uSmSQze/bsGXK2kqRhTGKN5MXAq5M8AFwN/EKSDwOPtM1VtOdH2/i7gJMGpl8DPNzqa+ap76eqLq+qDVW1YdWqVUv5XSTpsDf2IKmqi6pqTVWtpduJ/rmqegOwHdjcRtsMXNeGtwObkhyRZB3dTvWb2+avvUlOb0drnTcwjSRpTIa5H8m4XAJcm+R8uisOnwtQVXcmuRa4C9gHXNhuuAVwAXAlcCRwfXtIksZookFSVV8AvtCGvwWcscB4FwMXz1OfAZ47ug4lSQfime2SpF4MEklSLwaJJKkXg0SS1ItBIknqxSCRJPVikEiSejFIJEm9GCSSpF4MEklSLwaJJKkXg0SS1ItBIknqxSCRJPVikEiSejFIJEm9GCSSpF4MEklSLwaJJKkXg0SS1ItBIknqxSCRJPVikEiSejFIJEm9GCSSpF4MEklSLwaJJKkXg0SS1MvYgyTJSUk+n+TuJHcmeXOrH5vkhiT3tudjBqa5KMnOJPckOXOgfmqS29t7lybJuL+PJB3uJrFGsg/4N1X1M8DpwIVJTga2Ajuqaj2wo72mvbcJOAU4C3hvkhXtsy4DtgDr2+OscX4RSdIEgqSqdlfVrW14L3A3sBrYCGxro20DzmnDG4Grq+rxqrof2AmcluRE4OiqurGqCrhqYBpJ0phMdB9JkrXA84GbgBOqajd0YQMc30ZbDTw0MNmuVlvdhufWJUljNLEgSfI04BPAW6rqscVGnadWi9Tnm9eWJDNJZvbs2XPwzUqSFjSRIEnyo3Qh8pGq+mQrP9I2V9GeH231XcBJA5OvAR5u9TXz1PdTVZdX1Yaq2rBq1aql+yKSJFaOe4btyKoPAHdX1XsG3toObAYuac/XDdQ/muQ9wE/Q7VS/uaqeSLI3yel0m8bOA35vTF/jsLF266cnNu8HLjl7YvOWNLyxBwnwYuCfAbcnua3Vfp0uQK5Ncj7wIHAuQFXdmeRa4C66I74urKon2nQXAFcCRwLXt4ckaYzGHiRV9T+Zf/8GwBkLTHMxcPE89RnguUvXnSTpYHlmuySpF4NEktSLQSJJ6sUgkST1YpBIknoxSCRJvRgkkqReDBJJUi8GiSSpF4NEktSLQSJJ6sUgkST1YpBIknoxSCRJvRgkkqReDBJJUi8GiSSpF4NEktTLJO7ZLg1l7dZPT2S+D1xy9kTmK00r10gkSb0YJJKkXgwSSVIvBokkqReDRJLUi0EiSerFIJEk9eJ5JNIcnr8iHRzXSCRJvbhGIi0Tk1oTAteG1M/Ur5EkOSvJPUl2Jtk66X4k6XAz1UGSZAXw+8DLgZOB1yc5ebJdSdLhZaqDBDgN2FlV91XVXwFXAxsn3JMkHVamfR/JauChgde7gL8/d6QkW4At7eX3k9xzkPM5DvjmIXU4XtPQpz0unSXrM+9cik+Z1zQsS3sczk8u9Ma0B0nmqdV+harLgcsPeSbJTFVtONTpx2Ua+rTHpTMNfdrj0ljuPU77pq1dwEkDr9cAD0+oF0k6LE17kHwRWJ9kXZKnAJuA7RPuSZIOK1O9aauq9iX5FeAzwArgiqq6cwSzOuTNYmM2DX3a49KZhj7tcWks6x5Ttd8uBUmShjbtm7YkSRNmkEiSejFIDmC5XIIlyUlJPp/k7iR3Jnlzq78jyTeS3NYerxiY5qLW9z1JzhxTnw8kub31MtNqxya5Icm97fmYCff40wPL67YkjyV5y6SXZZIrkjya5I6B2kEvuySntt9gZ5JLk8x3mPxS9vhbSb6a5CtJPpXkGa2+Nsn/HVie7xtHj4v0edC/7wSW5TUD/T2Q5LZWn9iyHEpV+VjgQbcD/2vAs4CnAF8GTp5QLycCL2jDTwf+D91lYd4B/Oo845/c+j0CWNe+x4ox9PkAcNyc2ruArW14K/DOSfY4z2/853QnW010WQIvAV4A3NFn2QE3Ay+kO8/qeuDlI+7xZcDKNvzOgR7XDo4353NG1uMifR707zvuZTnn/XcDb5v0shzm4RrJ4pbNJViqandV3dqG9wJ3053Zv5CNwNVV9XhV3Q/spPs+k7AR2NaGtwHnDNQn3eMZwNeq6uuLjDOWPqvqT4FvzzPvoZddkhOBo6vqxur+lblqYJqR9FhVn62qfe3ln9Gdz7WgUfe4UJ+LWDbLclZbq/inwMcW+4xxLMthGCSLm+8SLIv94z0WSdYCzwduaqVfaZsVrhjY9DGp3gv4bJJb0l2aBuCEqtoNXSACx0+4x0GbePJf1uW0LOHgl93qNjy3Pi5vpPtf8ax1Sb6U5H8k+flWm2SPB/P7TrLPnwceqap7B2rLbVn+kEGyuKEuwTJOSZ4GfAJ4S1U9BlwG/BTwPGA33eowTK73F1fVC+iuyHxhkpcsMu5El2+6k1hfDfzXVlpuy3IxC/U0sV6TvBXYB3yklXYDf6eqng/8a+CjSY6eYI8H+/tO8nd/PU/+D85yW5ZPYpAsblldgiXJj9KFyEeq6pMAVfVIVT1RVX8NvJ+/2eQykd6r6uH2/CjwqdbPI20VfHZV/NFJ9jjg5cCtVfUILL9l2RzsstvFkzctjaXXJJuBVwK/2Dax0DYVfasN30K37+HZk+rxEH7fSS3LlcBrgGtma8ttWc5lkCxu2VyCpW0z/QBwd1W9Z6B+4sBo/xiYPQJkO7ApyRFJ1gHr6XbKjbLHpyZ5+uww3U7YO1ovm9tom4HrJtXjHE/6X99yWpYDDmrZtc1fe5Oc3v7MnDcwzUgkOQv4NeDVVfWXA/VV6e4ZRJJntR7vm0SPrYeD+n0n1SfwUuCrVfXDTVbLbVnuZ9x796ftAbyC7giprwFvnWAf/4BulfUrwG3t8QrgQ8Dtrb4dOHFgmre2vu9hDEdy0B3d9uX2uHN2eQHPBHYA97bnYyfV48B8jwK+Bfz4QG2iy5Iu1HYDP6D7n+b5h7LsgA10/0h+DfgvtCtYjLDHnXT7GGb/XL6vjftP2p+DLwO3Aq8aR4+L9HnQv++4l2WrXwn88pxxJ7Ysh3l4iRRJUi9u2pIk9WKQSJJ6MUgkSb0YJJKkXgwSSVIvBom0hJJ8fwSf+bw5V6p9R5JfXer5SIfKIJGWv+fRnTMkLUsGiTQiSf5tki+2iwT+RqutTXdPmfenu6/MZ5Mc2d77uTbujenu8XFHu6LCbwKva/eheF37+JOTfCHJfUneNKGvKAEGiTQSSV5GdxmL0+jWKE4duIDleuD3q+oU4Lt0Zy0DfJDujOYXAk8AVHf7grcB11TV86pq9vpLzwHObJ//9nYdNmkiDBJpNF7WHl+iu6TFc+gCBOD+qrqtDd8CrE13V8GnV9X/bvWPHuDzP13dhfy+SXchxxOWsHfpoKycdAPS31IB/lNV/cGTit29ZB4fKD0BHMn8lwNfzNzP8O+yJsY1Emk0PgO8sd0/hiSrkxy/0MhV9R3aVVxbadPA23vpbq8sLUsGiTQCVfVZus1TNya5Hfg4Bw6D84HLk9xIt4byvVb/PN3O9cGd7dKy4dV/pWUiydOq6vtteCvdZc7fPOG2pANyu6q0fJyd5CK6v5dfB/75ZNuRhuMaiSSpF/eRSJJ6MUgkSb0YJJKkXgwSSVIvBokkqZf/D6oz+3Vv8UhNAAAAAElFTkSuQmCC"/> + +#### 시퀀스 패딩 + ++ 서로 다른 개수의 단어로 이루어진 문장을 같은 길이로 만들어주기 위해 패딩을 사용합니다. + ++ 패딩을 사용하기 위해서 tensorflow.keras.preprocessing.sequence 모듈의 pad_sequences함수를 사용합니다. + ++ pad_sequences 함수는 숫자 0을 이용해서 같은 길이의 시퀀스로 변환합니다. + + + +<br/> + + + +###### padding 파라미터 + ++ 'post' : 시퀀스의 뒤에 패딩이 채워집니다. 디폴트는 'pre'입니다. + ++ maxlen 파라미터 + + + 시퀀스의 최대 길이를 제한합니다. + ++ truncating 파라미터 + + + 최대 길이를 넘는 시퀀스를 잘라낼 위치를 지정합니다. + + + 'post'로 지정하면 뒷부분을 잘라냅니다. + + + +<br/> + + + +pad_sequences()의 인자 maxlen을 통해 패딩할 길이를 설정합니다. + +위에서 데이터의 토큰 길이를 시각화해보고 100이 적절하다고 생각하여 maxlen을 100으로 설정합니다. + + + +```python +from tensorflow.keras.preprocessing.sequence import pad_sequences + +max_len = 100 + +X_train_seq = pad_sequences(X_train, maxlen=max_len) +X_val_seq = pad_sequences(X_val, maxlen=max_len) + +X_train_seq.shape, X_val_seq.shape +``` + +<pre> +((20000, 100), (5000, 100)) +</pre> +#### 원 핫 인코딩 + +단어 집합의 크길르 벡터 차원으로 만들어줍니다. + +표현하고 싶은 단어의 인덱스에 1의 값을 부여하고, 나머지는 0을 부여하는 벡터 표현 방식입니다. + + + +```python +from tensorflow import keras + +X_train_seq = keras.utils.to_categorical(X_train_seq) +X_val_seq = keras.utils.to_categorical(X_val_seq) + +X_train_seq.shape, X_val_seq.shape +``` + +<pre> +((20000, 100, 500), (5000, 100, 500)) +</pre> +#### 순환 신경망 모델 만들기 + + + +```python +model = keras.Sequential() + +model.add(keras.layers.SimpleRNN(8, input_shape=(100, 500))) +model.add(keras.layers.Dense(1, activation='sigmoid')) +``` + +단순 순환 신경망 하나로 이루어진 간단한 모델입니다. + +은닉층 크기는 8, 입력 크기는 원 핫 인코딩을 끝낸 벡터의 차원을 입력해서 SimpleRNN을 사용합니다. + +RNN층 다음에 Dense층을 사용할 때는 합성곱층과 달리 Flatten층을 사용할 필요가 없습니다. + +Dense층은 긍정/부정 두가지 레이블을 판별하므로 sigmoid를 사용했습니다. + + + +```python +model.summary() +``` + +<pre> +Model: "sequential" +_________________________________________________________________ + Layer (type) Output Shape Param # +================================================================= + simple_rnn (SimpleRNN) (None, 8) 4072 + + dense (Dense) (None, 1) 9 + +================================================================= +Total params: 4,081 +Trainable params: 4,081 +Non-trainable params: 0 +_________________________________________________________________ +</pre> +순환신경망의 출력 벡터는 입력 벡터의 크기와 동일하고, 8개의 뉴런과 500개의 원핫 인코딩 입력이 완전 연결되므로 500 x 8, + +은닉 상태에서 8개의 뉴런이 완전 연결되도록 순환하므로 8 x 8, 8개의 절편이 존재하므로 +8이 되어 4072개가 됩니다. + +Dense층에서 8 x 1 + 1이므로 9개가 되어 총 4081개의 파라미터를 확인 할 수 있습니다. + + + + +```python +rmsprop = keras.optimizers.RMSprop(learning_rate=1e-4) + +model.compile(optimizer=rmsprop, + loss='binary_crossentropy', + metrics=['acc']) +``` + + +```python +checkpoint = keras.callbacks.ModelCheckpoint('./models/imdb-simplernn-model.h5') +early_stopping = keras.callbacks.EarlyStopping(patience=3, + restore_best_weights=True) + +history = model.fit(X_train_seq, y_train, epochs=100, batch_size=64, + validation_data=(X_val_seq, y_val), + callbacks=[checkpoint, early_stopping]) +``` + + +```python +loss = history.history['loss'] +val_loss = history.history['val_loss'] +acc = history.history['acc'] +val_acc = history.history['val_acc'] + +epochs = range(1, len(loss) + 1) +fig = plt.figure(figsize=(10, 5)) + +ax1 = fig.add_subplot(1, 2, 1) +ax1.plot(epochs, loss, color='blue', label='train_loss') +ax1.plot(epochs, val_loss, color='orange', label='val_loss') +ax1.set_title('train and val loss') +ax1.set_xlabel('epochs') +ax1.set_ylabel('loss') +ax1.legend() + +ax2 = fig.add_subplot(1, 2, 2) +ax2.plot(epochs, acc, color='green', label='train_acc') +ax2.plot(epochs, val_acc, color='red', label='val_acc') +ax2.set_title('train and val acc') +ax2.set_xlabel('epochs') +ax2.set_ylabel('acc') +ax2.legend() + +model.evaluate(X_val_seq, y_val) +``` + +<pre> +157/157 [==============================] - 2s 13ms/step - loss: 0.4702 - acc: 0.7810 +</pre> +<pre> +[0.47018691897392273, 0.781000018119812] +</pre> +<img src="data:image/png;base64,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"/> + +단순히 SimpleRNN 하나의 층만을 사용해서 약 0.78 정도의 정확도를 얻을 수 있었습니다. + +결과를 시각화해보면 40번째 epoch에서 과적합이 심해져 조기 종료되었음을 확인 할 수 있습니다. + + + +<br/> + + + +원 핫 인코딩을 사용해서 입력 데이터를 준비한다면, 단어 사전을 유연하게 늘리기가 어렵습니다. + +단어 사전이 증가할수록 또는 토큰의 개수가 늘어날수록 벡터 차원의 개수가 매우 많이 늘어나기 때문입니다. + +또한, 원 핫 인코딩은 하나의 벡터 원소만 1이고 나머진 0으로 채워지기 때문에 각 토큰 사이의 관련성을 알기 어렵습니다. + +이에 대비해서 임베딩 방법이 존재합니다. + +임베딩은 토큰들을 지정된 갯수의 실수 벡터 즉, 밀집 벡터로 변환해줍니다. + +원 핫 인코딩에 비해 단어 사이에 의미 있는 정보를 얻을 수 있는 방법입니다. + +임베딩을 사용하여 두번째 모델을 만들어 보겠습니다. + + + +<br/> + + + +Embedding() + ++ 단어를 밀집 벡터로 만드는 작업을 워드 임베딩(word embedding)이라고 합니다. + ++ 원-핫 인코딩과 상대적으로 저차원을 가지며 모든 원소의 값이 실수입니다. + + + 첫번째 인자 : 단어 사전의 크기(총 단어의 개수) + + + 두번째 인자 : 임베딩 벡터의 출력 차원(결과로 나오는 임베딩 벡터의 크기) + + 만약, embedding층과 연결된 층이 순환신경망이면 사용하지 않습니다. + + + input_length : time step의 길이(입력 시퀀스의 길이) + + 만약, 다음에 Flatten층이 오게 되면 반드시 input_length를 명시해주어야 합니다. + + + +```python +model2 = keras.Sequential() + +model2.add(keras.layers.Embedding(500, 16, input_length=100)) +model2.add(keras.layers.SimpleRNN(8)) +model2.add(keras.layers.Dense(1, activation='sigmoid')) + +model2.summary() +``` + +<pre> +Model: "sequential_1" +_________________________________________________________________ + Layer (type) Output Shape Param # +================================================================= + embedding (Embedding) (None, 100, 16) 8000 + + simple_rnn_1 (SimpleRNN) (None, 8) 200 + + dense_1 (Dense) (None, 1) 9 + +================================================================= +Total params: 8,209 +Trainable params: 8,209 +Non-trainable params: 0 +_________________________________________________________________ +</pre> +500개의 입력데이터가 Embedding층을 통과하면 16개의 벡터 크기로 출력됩니다. + +그러므로 SimpleRNN층에서 처리하는 벡터의 갯수가 훨씬 줄어들게 됩니다. + +input_length를 100으로 지정해서 입력 토큰 즉, time step의 갯수를 지정해줍니다. + + + +<br/> + + + +summary의 결과입니다. + +Embedding층에서 500개의 입력 데이터와 16개의 뉴런이 완전 연결되어 + +100 x 16개의 파라미터가 사용되고, 16개의 출력 벡터가 나옵니다. + +SimpleRNN층에서 16개의 입력 벡터와 8개의 뉴런이 완전 연결되어 16 x 8, + +다시 순환하여 8 x 8, 8개의 절편을 포함하여 총 200개의 파라미터가 사용되고, + +마지막 Dense층에서 파라미터의 개수가 8 x 1 + 1이 됨을 확인할 수 있습니다. + + + +```python +max_len = 100 + +X_train_seq = pad_sequences(X_train, maxlen=max_len) +X_val_seq = pad_sequences(X_val, maxlen=max_len) + +checkpoint = keras.callbacks.ModelCheckpoint('./models/imdb-embedding-simplernn-model.h5') +early_stopping = keras.callbacks.EarlyStopping(patience=3, + restore_best_weights=True) +``` + + +```python +model2.compile(optimizer=rmsprop, + loss='binary_crossentropy', + metrics=['acc']) + +history2 = model2.fit(X_train_seq, y_train, epochs=100, batch_size=64, verbose=0, + validation_data=(X_val_seq, y_val), + callbacks=[checkpoint, early_stopping]) +``` + + +```python +model2.evaluate(X_val_seq, y_val) +``` + +<pre> +157/157 [==============================] - 0s 3ms/step - loss: 0.4535 - acc: 0.7884 +</pre> +<pre> +[0.4534836411476135, 0.7883999943733215] +</pre> + +```python +loss2 = history2.history['loss'] +val_loss2 = history2.history['val_loss'] +loss = history2.history['loss'] +val_loss = history2.history['val_loss'] + +epochs = range(1, len(loss) + 1) +fig = plt.figure(figsize=(10, 5)) + +ax1 = fig.add_subplot(1, 2, 1) +ax1.plot(epochs, loss, color='blue', label='train_loss') +ax1.plot(epochs, val_loss, color='orange', label='val_loss') +ax1.set_title('Model1 loss') +ax1.set_xlabel('epochs') +ax1.set_ylabel('loss') +ax1.legend() + +ax2 = fig.add_subplot(1, 2, 2) +ax2.plot(epochs, loss2, color='green', label='train_acc') +ax2.plot(epochs, val_loss2, color='red', label='val_acc') +ax2.set_title('Model2 loss') +ax2.set_xlabel('epochs') +ax2.set_ylabel('loss') +ax2.legend() +``` + +<pre> +<matplotlib.legend.Legend at 0x7fd1d3ea7730> +</pre> +<img src="data:image/png;base64,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"/> + +위에서 데이터를 간소화하기 위해 단어의 갯수를 500개로 제한하고, 패딩의 길이를 100으로 설정했습니다. + +임베딩의 효과를 더 보기 위해 이번엔 단어의 갯수를 10000개로 늘리고 패딩의 길이를 500으로 설정하여 + +모델을 구성해보겠습니다. + + + +```python +# 데이터 불러오기, 단어 사전의 길이를 10000개로 지정 +num_word = 10000 + +(X_train, y_train), (X_test, y_test) = imdb.load_data(num_words=num_word) +``` + + +```python +# 시퀀스 패딩하기 +X_train_seq = pad_sequences(X_train, maxlen=500) +X_test_seq = pad_sequences(X_test, maxlen=500) + +# 검증 셋 분리 +X_train_seq, X_val_seq, y_train, y_val = train_test_split(X_train_seq, y_train) + +X_train_seq.shape, X_val_seq.shape +``` + +<pre> +((18750, 500), (6250, 500)) +</pre> + +```python +# 모델 정의 및 컴파일 +model = keras.Sequential() + +model.add(keras.layers.Embedding(10000, 16, input_length=500)) +model.add(keras.layers.SimpleRNN(8)) +model.add(keras.layers.Dense(1, activation='sigmoid')) + +model.compile(optimizer=rmsprop, + loss='binary_crossentropy', + metrics=['acc']) +``` + + +```python +# 콜백, 모델 학습 +checkpoint = keras.callbacks.ModelCheckpoint('./models/imdb10000-simplernn-embedding.h5') +early_stopping = keras.callbacks.EarlyStopping(patience=3, restore_best_weights=True) + +history = model.fit(X_train_seq, y_train, epochs=100, batch_size=64, verbose=0, + validation_data=(X_val_seq, y_val), + callbacks=[checkpoint, early_stopping]) +``` + + +```python +# 손실과 정확도 시각화 +loss = history.history['loss'] +val_loss = history.history['val_loss'] +acc = history.history['acc'] +val_acc = history.history['val_acc'] + +epochs = range(1, len(loss) + 1) +fig = plt.figure(figsize=(10, 5)) + +ax1 = fig.add_subplot(1, 2, 1) +ax1.plot(epochs, loss, color='blue', label='train_loss') +ax1.plot(epochs, val_loss, color='orange', label='val_loss') +ax1.set_title('train and val loss') +ax1.set_xlabel('epochs') +ax1.set_ylabel('loss') +ax1.legend() + +ax2 = fig.add_subplot(1, 2, 2) +ax2.plot(epochs, acc, color='green', label='train_acc') +ax2.plot(epochs, val_acc, color='red', label='val_acc') +ax2.set_title('train and val acc') +ax2.set_xlabel('epochs') +ax2.set_ylabel('acc') +ax2.legend() +``` + +<pre> +<matplotlib.legend.Legend at 0x7fd32a4448b0> +</pre> +<img src="data:image/png;base64,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"/> + + +```python +# 모델 평가 +model.evaluate(X_test_seq, y_test) +``` + +<pre> +782/782 [==============================] - 9s 11ms/step - loss: 0.3963 - acc: 0.8402 +</pre> +<pre> +[0.39633744955062866, 0.8401600122451782] +</pre> +모델 학습 과정에서 손실과 정확도를 시각화해보면 검증 셋에서 약 0.84정도의 정확도를 얻었습니다. + +epoch가 약 30에서 과적합이 심해져 조기 종료 되었음을 확인 할 수 있습니다. + +테스트 데이터에 모델을 적용한 결과 약 0.84의 정확도의 나쁘지 않은 결과를 얻을 수 있었습니다. + diff --git "a/_posts/2022-02-12-LSTM, GRU(imdb \354\230\210\354\270\241).md" "b/_posts/2022-02-12-LSTM, GRU(imdb \354\230\210\354\270\241).md" new file mode 100644 index 000000000000..48f6ebdf29b3 --- /dev/null +++ "b/_posts/2022-02-12-LSTM, GRU(imdb \354\230\210\354\270\241).md" @@ -0,0 +1,625 @@ +--- +layout: single +title: "LSTM과 GRU를 사용한 IMDB 분류" +categories: 딥러닝 +tag: [python, 파이썬, 딥러닝, 순환신경망, LSTM, GRU, IMDB] +toc: true +toc_sticky: true + +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 8px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + + .output_prompt { + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; + -webkit-overflow-scrolling: touch; + padding: 0.8rem; + margin-top: 0; + margin-bottom: 15px; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: $code-text-color; + border: solid 1px $border-color; + border-radius: 0.3rem; + word-break: normal; + white-space: pre; + } + + .dataframe tbody tr th:only-of-type { + vertical-align: middle; + } + + .dataframe tbody tr th { + vertical-align: top; + } + + .dataframe thead th { + text-align: center !important; + padding: 8px; + } + + .page__content p { + margin: 0 0 0px !important; + } + + .page__content p > strong { + font-size: 0.8rem !important; + } + + </style> +</head> + + +#### LSTM + + + +RNN은 관련 정보와 그 정보를 사용하는 지점 사이 거리가 멀 경우 역전파시 그래디언트가 점차 줄어 학습능력이 떨어진다고 알려져 있습니다. (vanishing gradient problem) + +이 문제를 보완하기 위해 LSTM이 등장합니다. + + + +LSTM(Long Short Term Memory)는 기존의 RNN이 출력과 먼 위치에 있는 정보를 기억할 수 없다는 단점을 보완한 방법입니다. + +장기 / 단기 기억을 가능하게 설계한 신경망의 구조입니다. + +주로 시계열, 자연어 처리에 사용됩니다. + + + +<br/> + + + +#### LSTM의 구조 + + + +LSTM은 RNN과 다르게 한 개의 tanh layer가 아닌 4개의 layer로 구성되어 서로 정보를 주고 받는 방식입니다. + +또한, LSTM 셀에서는 상태가 크게 두 가지의 상태가 순환합니다. + +RNN의 은닉 상태(hidden state)에 셀 상태(cell state)가 추가됩니다. + +LSTM의 출력으로 사용되는 것은 은닉 상태, 셀 상태는 셀 안에서 순환하는 상태입니다. + + + + + +<이미지 출처 : https://imgur.com/jKodJ1u> + + + +여기서 LSTM 모듈에는 4개의 layer가 존재하는 것을 알 수 있습니다. + + + +<br/> + + + +#### LSTM 과정 + + + + + +노란색 박스는 학습된 neural network layer이고, 분홍색 동그라미는 vector 연산과 같은 pointwise operation을 나타냅니다. + +합쳐지는 선은 concatenateion을 의미하고, 갈라지는 선은 정보를 복사해서 다른쪽으로 보내는 fork입니다. + + + +<br/> + + + + + + + +Cell state는 모듈 상단에 수평으로 그어진 윗 선에 해당됩니다. + +Cell state는 컨베이어 벨트와 같이, 정보가 전혀 바뀌지 않고 그대로 흐르게만 하는 역할을 합니다. + +LSTM은 Cell state에 뭔가를 더하거나 없애는 과정을 반복합니다. 이 과정에서 3가지의 gate를 사용합니다. + + + +<br/> + + + + + + + +LSTM의 첫 단계로 Cell state로부터 어떤 정보를 버릴 것인지 정하는 forget gate 입니다. + +ht-1과 xt를 받아 시그모이드를 취해(0 ~ 1값) ct-1에 보내줍니다. + +이 값이 1이면 "모든 정보 전달", "0이면 모두 버리기"가 됩니다. + + + +<br/> + + + + + + + +다음 단계는 입력되는 새로운 정보 중 어떤 것을 Cell state에 저장할 것인지 정하는 input gate 입니다. + +먼저 sigmoid layer가 어떤 값을 업데이트할 지 정하고, 그 다음에 tanh layer가 새로운 후보 값 c~t라는 vector를 만들고 Cell state에 더할 준비를 합니다. + +이렇게 두 단계에서 나온 정보를 합쳐서 state에 업데이트할 준비를 합니다. + + + +<br/> + + + + + + + +이전 state인 ct-1에 ft를 곱해서 forget gate에서 잊어버리기로 정했던 정보들을 모두 잊어버립니다. + +그리고나서 it x c~t를 더해줍니다. 이 더한 값은 두번째 단계에서 업데이트하기로 한 값을 얼마나 업데이트할 지 정한 만큼 scale한 값이 됩니다. + + + +<br/> + + + + + + + +마지막으로 출력으로 내보내는 output gate 입니다. + +먼저, sigmoid layer에 input 데이터를 넣어서 Cell state의 어느 부분을 ouput으로 보낼지 정합니다. + +그리고 Cell state를 tanh layer에 전달해서 -1과 1사이의 값을 받은 뒤에 방금 전의 sigmoid layer의 output과 곱합니다. + +그 후, ouput으로 보내게 됩니다. + + +#### GRU 셀 + +GRU 셀은 LSTM 셀에서 좀 더 간소화된 구조라고 말할 수 있습니다. + +LSTM에 비해 Gate가 2개이며 reset gate(r)와 update gate(z)로 이루어집니다. + ++ reset gate는 이전 상태를 얼마나 잊어버릴지를 결정합니다. + ++ update gate는 이전 상태와 현재 상태를 얼마만큼의 비율로 반영할지를 결정합니다. + + + +LSTM 셀에 존재하는 cell state와 hidden state가 하나로 합쳐집니다. + + + + + + + +Input + +이전 시점의 셀에서 전달된 hidden state(h(t-1))와 새로운 input값인 x(t)가 합쳐지고 두 방향으로 나뉘어 전달됩니다. + +하나는 게이트에 전달될 정보가 되고, 다른 하나는 LSTM의 Candidate state의 역할을 합니다. + + + +reset gate + +이전 상태의 은닉 상태와 현재 상태의 x를 받아 sigmoid처리를 합니다. + +candidate state(h_hat(t))에 전달되는 데이터에 어떤 정보를 지우고, 어떤 정보를 전달할지 결정합니다. + + + +update gate + +이전 상태의 은닉 상태와 현재 상태의 x를 받아 sigmoid처리를 합니다. + +LSTM 셀의 forget과 input 게이트와 비슷한 역할을 하지만, Update 되는 정보의 양에 1이라는 제한이 있습니다. + +이전 정보인 h(t-1)과 reset gate를 지나온 candidate state(h_hat(t))에 동시에 전달되는데, + +이때 한쪽에 x의 크기가 전달되면, 반대쪽은 1-x만큼 전달됩니다. + +즉, 정보를 지운만큼만 새로운 정보를 입력하고, 새로운 정보를 입력한 만큼 정보를 지울 수 있습니다. + + + +candidate state + +현재 셀에 입력된 정보 중에서 Output과 다음 셀로 전달할 중요한 후보 정보를 담고 있는 state입니다. + +reset gate를 거친 정보들은 tanh함수를 취해 update gate까지 지나며 h(t-1)과 합쳐집니다. + + + +hidden state + +LSTM의 cell state의 역할까지 동시에 합니다. + +update gate를 통과하면서 의미 없는 데이터를 삭제하고 현재 셀의 중요한 정보를 담고 있는 candidate state와 + +합쳐져 output이 되고, 동시에 다음 시점의 셀로 정보를 전달합니다. + + +#### LSTM 신경망으로 IMDB 예측 + + + +이전에 SimpleRNN과 Embedding을 사용하여 예측했던 IMDB 데이터 분류를 LSTM을 사용하여 다시 분류해보겠습니다. + +데이터 처리 과정은 이전과 동일하게 수행하겠습니다. + + + +```python +from tensorflow import keras +import numpy as np +import matplotlib.pyplot as plt +from sklearn.model_selection import train_test_split +from tensorflow.keras.preprocessing.sequence import pad_sequences + +(X_train, y_train), (X_test, y_test) = keras.datasets.imdb.load_data(num_words=10000) + +X_train_seq = pad_sequences(X_train, maxlen=500) +X_test_seq = pad_sequences(X_test, maxlen=500) + +X_train_seq, X_val_seq, y_train, y_val = train_test_split(X_train_seq, y_train) + +X_train_seq.shape, X_val_seq.shape +``` + +<pre> +((18750, 500), (6250, 500)) +</pre> + +```python +model = keras.Sequential() + +model.add(keras.layers.Embedding(10000, 16, input_length=500)) +model.add(keras.layers.LSTM(8)) +model.add(keras.layers.Dense(1, activation='sigmoid')) + +model.summary() +``` + +<pre> +Model: "sequential_2" +_________________________________________________________________ + Layer (type) Output Shape Param # +================================================================= + embedding_2 (Embedding) (None, 500, 16) 160000 + + lstm_2 (LSTM) (None, 8) 800 + + dense_1 (Dense) (None, 1) 9 + +================================================================= +Total params: 160,809 +Trainable params: 160,809 +Non-trainable params: 0 +_________________________________________________________________ +</pre> +summary 정보에서 파라미터 갯수를 확인해보면 embedding층에서는 10000 x 16개, + +LSTM층에서는 embedding층의 출력인 16이 입력이 되어 8개의 뉴런과 완전 연결되어 16 x 8, + +은닉 상태의 셀이 순환되어 8 x 8, 그리고 절편 8개를 포함하여 200개가 되는데 LSTM은 4개의 layer가 존재하므로 + +200 x 4 = 800이 되는 것을 확인할 수 있습니다. + + + +```python +rmsprop = keras.optimizers.RMSprop(learning_rate=1e-4) + +model.compile(optimizer=rmsprop, + loss='binary_crossentropy', + metrics=['acc']) + +checkpoint = keras.callbacks.ModelCheckpoint('./models/imdb10000-lstm-model.h5') +early_stopping = keras.callbacks.EarlyStopping(patience=3, restore_best_weights=True) + +history = model.fit(X_train_seq, y_train, epochs=100, batch_size=64, verbose=0, + validation_data=(X_val_seq, y_val), + callbacks=[checkpoint, early_stopping]) +``` + + +```python +model.evaluate(X_test_seq, y_test) +``` + +<pre> +782/782 [==============================] - 13s 17ms/step - loss: 0.3348 - acc: 0.8679 +</pre> +<pre> +[0.33479073643684387, 0.8679199814796448] +</pre> + +```python +# 손실과 정확도 시각화 +loss = history.history['loss'] +val_loss = history.history['val_loss'] +acc = history.history['acc'] +val_acc = history.history['val_acc'] + +epochs = range(1, len(loss) + 1) +fig = plt.figure(figsize=(10, 5)) + +ax1 = fig.add_subplot(1, 2, 1) +ax1.plot(epochs, loss, color='blue', label='train_loss') +ax1.plot(epochs, val_loss, color='orange', label='val_loss') +ax1.set_title('train and val loss') +ax1.set_xlabel('epochs') +ax1.set_ylabel('loss') +ax1.legend() + +ax2 = fig.add_subplot(1, 2, 2) +ax2.plot(epochs, acc, color='green', label='train_acc') +ax2.plot(epochs, val_acc, color='red', label='val_acc') +ax2.set_title('train and val acc') +ax2.set_xlabel('epochs') +ax2.set_ylabel('acc') +ax2.legend() +``` + +<pre> +<matplotlib.legend.Legend at 0x7f9f33e88520> +</pre> +<img src="data:image/png;base64,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"/> + +<br/> + +#### Dropout 추가 + + + +이전에 구성했던 모델을 좀 더 정확한 예측을 위해 신경망을 좀 더 깊게 수정하고 Dropout을 추가해보았습니다. + +LSTM층에서 다음 LSTM층을 쌓아 연결할때는, 각 타임 스텝의 은닉상태를 모두 출력해주어야 마지막에 있는 순환 셀이 제대로 동작할 것입니다. + +기본적으로 keras의 순환층은 마지막 타임 스텝의 은닉상태만 출력하기 때문에 모든 타임 스텝마다 은닉상태를 출력해주기 위해 return_sequences=True를 설정해주어야 합니다. + +LSTM에서 Dropout을 사용할 땐 따로 층을 추가하지 않고 매개변수로서 추가를 해줘야 합니다. + + + +```python +from tensorflow import keras +import numpy as np +import matplotlib.pyplot as plt +from sklearn.model_selection import train_test_split +from tensorflow.keras.preprocessing.sequence import pad_sequences + +(X_train, y_train), (X_test, y_test) = keras.datasets.imdb.load_data(num_words=10000) + +X_train_seq = pad_sequences(X_train, maxlen=500) +X_test_seq = pad_sequences(X_test, maxlen=500) + +X_train_seq, X_val_seq, y_train, y_val = train_test_split(X_train_seq, y_train) + +X_train_seq.shape, X_val_seq.shape +``` + +<pre> +Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/imdb.npz +17465344/17464789 [==============================] - 0s 0us/step +17473536/17464789 [==============================] - 0s 0us/step +</pre> +<pre> +((18750, 500), (6250, 500)) +</pre> + +```python +model2 = keras.Sequential() + +model2.add(keras.layers.Embedding(10000, 16, input_length=500)) +model2.add(keras.layers.LSTM(8, dropout=0.3, return_sequences=True)) +model2.add(keras.layers.LSTM(8, dropout=0.3)) +model2.add(keras.layers.Dense(1, activation='sigmoid')) + +model2.summary() +``` + + +```python +rmsprop = keras.optimizers.RMSprop(learning_rate=1e-4) + +model2.compile(optimizer=rmsprop, + loss='binary_crossentropy', + metrics=['acc']) + +checkpoint = keras.callbacks.ModelCheckpoint('/content/gdrive/MyDrive/models/imdb10000-lstm-model.h5') +early_stopping = keras.callbacks.EarlyStopping(patience=3, restore_best_weights=True) + +history = model2.fit(X_train_seq, y_train, epochs=100, batch_size=64, verbose=0, + validation_data=(X_val_seq, y_val), + callbacks=[checkpoint, early_stopping]) +``` + + +```python +model2.evaluate(X_test_seq, y_test) +``` + +<pre> +782/782 [==============================] - 52s 67ms/step - loss: 0.3029 - acc: 0.8813 +</pre> +<pre> +[0.302852600812912, 0.8813199996948242] +</pre> + +```python +acc = history.history['acc'] +val_acc = history.history['val_acc'] +loss = history.history['loss'] +val_loss = history.history['val_loss'] + +epochs = range(1, len(loss) + 1) +fig = plt.figure(figsize=(10, 5)) + +ax1 = fig.add_subplot(1, 2, 1) +ax1.plot(epochs, acc, color='blue', label='train_loss') +ax1.plot(epochs, val_acc, color='orange', label='val_loss') +ax1.set_title('train and val acc') +ax1.set_xlabel('epochs') +ax1.set_ylabel('acc') +ax1.legend() + +ax2 = fig.add_subplot(1, 2, 2) +ax2.plot(epochs, loss, color='green', label='train_acc') +ax2.plot(epochs, val_loss, color='red', label='val_acc') +ax2.set_title('train and val loss') +ax2.set_xlabel('epochs') +ax2.set_ylabel('loss') +ax2.legend() +``` + +<pre> +<matplotlib.legend.Legend at 0x7f8da1562a50> +</pre> +<img src="data:image/png;base64,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"/> + +드롭아웃을 적용한 모델에서 훈련 데이터와 검증 데이터의 손실값 차이가 좀 더 적게 나타난 것을 확인할 수 있습니다. + +다음은 GRU셀을 이용해서 동일한 방법으로 모델을 구성해보았습니다. + + + +```python +model3 = keras.Sequential() + +model3.add(keras.layers.Embedding(10000, 16, input_length=500)) +model3.add(keras.layers.GRU(8, dropout=0.3, return_sequences=True)) +model3.add(keras.layers.GRU(8, dropout=0.3)) +model3.add(keras.layers.Dense(1, activation='sigmoid')) + +model3.summary() +``` + +<pre> +Model: "sequential_3" +_________________________________________________________________ + Layer (type) Output Shape Param # +================================================================= + embedding_3 (Embedding) (None, 500, 16) 160000 + + gru_2 (GRU) (None, 500, 8) 624 + + gru_3 (GRU) (None, 8) 432 + + dense_3 (Dense) (None, 1) 9 + +================================================================= +Total params: 161,065 +Trainable params: 161,065 +Non-trainable params: 0 +_________________________________________________________________ +</pre> + +```python +model3.compile(optimizer=rmsprop, + loss='binary_crossentropy', + metrics=['acc']) + +checkpoint = keras.callbacks.ModelCheckpoint('/content/gdrive/MyDrive/models/imdb10000-gru-model.h5') +early_stopping = keras.callbacks.EarlyStopping(patience=3, restore_best_weights=True) + +history2 = model3.fit(X_train_seq, y_train, epochs=100, batch_size=64, verbose=0, + validation_data=(X_val_seq, y_val), + callbacks=[checkpoint, early_stopping]) +``` + + +```python +model3.evaluate(X_test_seq, y_test) +``` + +<pre> +782/782 [==============================] - 49s 63ms/step - loss: 0.3073 - acc: 0.8774 +</pre> +<pre> +[0.30730995535850525, 0.8773599863052368] +</pre> + +```python +acc = history2.history['acc'] +val_acc = history2.history['val_acc'] +loss = history2.history['loss'] +val_loss = history2.history['val_loss'] + +epochs = range(1, len(loss) + 1) +fig = plt.figure(figsize=(10, 5)) + +ax1 = fig.add_subplot(1, 2, 1) +ax1.plot(epochs, acc, color='blue', label='train_loss') +ax1.plot(epochs, val_acc, color='orange', label='val_loss') +ax1.set_title('train and val acc') +ax1.set_xlabel('epochs') +ax1.set_ylabel('acc') +ax1.legend() + +ax2 = fig.add_subplot(1, 2, 2) +ax2.plot(epochs, loss, color='green', label='train_acc') +ax2.plot(epochs, val_loss, color='red', label='val_acc') +ax2.set_title('train and val loss') +ax2.set_xlabel('epochs') +ax2.set_ylabel('loss') +ax2.legend() +``` + +<pre> +<matplotlib.legend.Legend at 0x7f8da4c6aa90> +</pre> +<img src="data:image/png;base64,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"/> + +IMDB 데이터셋을 분류할 때, LSTM 셀을 사용한 방법과 GRU 셀을 사용한 방법이 비슷한 정확도를 가질 수 있는 것을 확인했습니다. + +실제로 GRU 셀을 사용했을 때 학습시간이 덜 걸린 것을 확인할 수 있었습니다. \ No newline at end of file diff --git a/_posts/2022-09-10-ml01_K-Nearest Neighbors(KNN).md b/_posts/2022-09-10-ml01_K-Nearest Neighbors(KNN).md new file mode 100644 index 000000000000..452125d12ad1 --- /dev/null +++ b/_posts/2022-09-10-ml01_K-Nearest Neighbors(KNN).md @@ -0,0 +1,253 @@ +--- +layout: single +title: "K-Nearest Neighbors(KNN)" +categories: 머신러닝 +tag: [KNN, 머신러닝, python] +toc: true +toc_sticky: true + +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 8px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + + .output_prompt { + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; + -webkit-overflow-scrolling: touch; + padding: 0.8rem; + margin-top: 0; + margin-bottom: 15px; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: $code-text-color; + border: solid 1px $border-color; + border-radius: 0.3rem; + word-break: normal; + white-space: pre; + } + + .dataframe tbody tr th:only-of-type { + vertical-align: middle; + } + + .dataframe tbody tr th { + vertical-align: top; + } + + .dataframe thead th { + text-align: center !important; + padding: 8px; + } + + .page__content p { + margin: 0 0 0px !important; + } + + .page__content p > strong { + font-size: 0.8rem !important; + } + + </style> +</head> + +## k-최근접 이웃 알고리즘이란? + +- 새로운 데이터 포인트와 가장 가까운 훈련 데이터셋의 데이터 포인트를 찾아 예측 +- **k 값에 따라 가까운 이웃의 수가 결정** +- **분류**와 **회귀**에 모두 사용 가능 + + + +## 특징 + +- **k 값이 작을 수록** 모델의 **복잡도가 상대적으로 증가**.(noise 값에 민감) +- 반대로 **k 값이 커질수록** 모델의 **복잡도가 낮아진다.** +- 100개의 데이터를 학습하고 k를 100개로 설정하여 예측하면 **빈도가 가장 많은 클래스** 레이블로 분류 +- 이해하기 매우 쉬운 모델 +- 훈련 데이터 세트가 크면(특성,샘플의 수) 예측이 느려진다 +- 수백 개 이상의 많은 특성을 가진 데이터 세트와 특성 값 대부분이 0인 희소(sparse)한 데이터 세트에는 잘 동작하지 않는다 +- 거리를 측정하기 때문에 같은 scale을 같도록 **정규화** 필요 + +## 데이터 포인트(sample) 사이 거리 값 측정 방법 + +- 유클리디언 거리공식 (Euclidean Distance) + + + +## scikit-learn 주요 매개변수 + +- metric : 유클리디언 거리 방식 +- n_neighbors : 이웃의 수 +- weight : 가중치 함수 + - uniform : 가중치를 동등하게 설정. + - distance : 가중치를 거리에 반비례하도록 설정 + +## KNN 알고리즘 직접 구현 + +### 과제 + +- 예측 데이터와 모든 학습 데이터(500개)와의 거리 계산 -> 유클리드 거리 계산 +- 계산된 모든 거리값 중 설정된 k값(k=5)만큼 가장 가까운 데이터 추출 +- 추출된 데이터의 비율 확인 후 예측 + +### 데이터 로딩 및 가공 + +```python +import pandas as pd + +data = pd.read_csv("./data/bmi_lbs.csv") +data['Weight(kg)'] = data['Weight(lbs)'] * 0.453592 # kg 단위로 변환 +``` + +### 학습 데이터 + +```python +X = data[['Height','Weight(kg)']] +y = data['Label'] +``` + +### 예측할 샘플 데이터 + +```python +sample_data = data.iloc[[30,110,150,433,498],[2,4]] +ground_truth = data.iloc[[30,110,150,433,498],0] +``` + +### KNN 함수 + +```python +# my_KNN +# 입력 변수 (예측할 데이터 샘플, k값) +def my_KNN(input_data, k): + # 카운터 딕셔너리 + from collections import Counter + + # 1. 모든 데이터와 input데이터 사이의 거리계산 + distance_list = [] + + for i in range(len(input_data)): + temp = (X - input_data.iloc[i]) ** 2 # X데이터 전체[키, 몸무게] - i번째 인풋[키, 몸무게] -> 결과 : [a제곱, b제곱] + distance = (temp.iloc[:, 0] + temp.iloc[:, 1])**0.5 # (키 + 몸무게) 의 제곱근 + + distance_list.append(distance) + distance_list = pd.Series(distance_list, index=input_data.index) # list -> series 변환, 크기 : 샘플데이터 갯수 X 500 + #print(distance_list) + + # 2. k값만큼 가장 가까운 거리의 데이터 추출 + k_neighbors = [] + + for i in range(len(input_data)): + input_i = distance_list.iloc[i] + temp = [] + for j in range(k): + # k개만큼 최솟값들 뽑기 + #idx = input_i[input_i == input_i.min()].index[0] + idx = input_i.idxmin() # 구한 최솟값의 인덱스 구하기 + temp.append(y.loc[idx]) + input_i.drop([idx], inplace=True) # 최솟값 중복 없애게 이번에 뽑은 최솟값은 제거해주기 + k_neighbors.append(temp) # k_neighbors -> 크기 : (샘플데이터 갯수 X k개의 최소거리 인덱스) + + #print(k_neighbor) + + # 3. 추출된 데이터의 비율 확인 후 예측 + output = [] + for nb in k_neighbors: + count = Counter(nb) # key : 결과, value: key값이 나온 갯수 + output.append(max(count,key=count.get)) + + output = pd.Series(output, index=input_data.index) # input값의 인덱스 유지하기 위해 series화 + return output +``` + +### 테스트 + +- 예측 + +```python +sample2 = data.iloc[[0,1,2,3,4],[2,4]] +my_KNN(sample2, 5) +``` + +``` +0 Obesity +1 Normal +2 Obesity +3 Overweight +4 Overweight +dtype: object +``` + +- 실제 정답 + +```python +y.iloc[[0,1,2,3,4]] +``` + +``` +0 Obesity +1 Normal +2 Obesity +3 Overweight +4 Overweight +Name: Label, dtype: object +``` + +- 예측 + +```python +sample3 = data.iloc[[12,16,19,21,31],[2,4]] +my_KNN(sample3, 5) +``` + +``` +12 Overweight +16 Extreme Obesity +19 Extreme Obesity +21 Extreme Obesity +31 Weak +dtype: object +``` + +- 실제 정답 + +```python +y.iloc[[12,16,19,21,31]] +``` + +``` +12 Overweight +16 Extreme Obesity +19 Extreme Obesity +21 Extreme Obesity +31 Weak +Name: Label, dtype: object +``` \ No newline at end of file diff --git "a/_posts/2022-09-15-ml02_\353\215\260\354\235\264\355\204\260 \354\212\244\354\274\200\354\235\274\353\247\201.md" "b/_posts/2022-09-15-ml02_\353\215\260\354\235\264\355\204\260 \354\212\244\354\274\200\354\235\274\353\247\201.md" new file mode 100644 index 000000000000..c98bc02266b3 --- /dev/null +++ "b/_posts/2022-09-15-ml02_\353\215\260\354\235\264\355\204\260 \354\212\244\354\274\200\354\235\274\353\247\201.md" @@ -0,0 +1,121 @@ +--- +layout: single +title: "데이터 스케일링(Data Scaling)" +categories: 머신러닝 +tag: [KNN, 머신러닝, python, 스케일링, Scaling] +toc: true +toc_sticky: true + +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 8px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + + .output_prompt { + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; + -webkit-overflow-scrolling: touch; + padding: 0.8rem; + margin-top: 0; + margin-bottom: 15px; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: $code-text-color; + border: solid 1px $border-color; + border-radius: 0.3rem; + word-break: normal; + white-space: pre; + } + + .dataframe tbody tr th:only-of-type { + vertical-align: middle; + } + + .dataframe tbody tr th { + vertical-align: top; + } + + .dataframe thead th { + text-align: center !important; + padding: 8px; + } + + .page__content p { + margin: 0 0 0px !important; + } + + .page__content p > strong { + font-size: 0.8rem !important; + } + + </style> +</head> + + +## 2. 데이터 스케일링이란? + +- **특성(Feature)들의 범위(range)를 정규화** 해주는 작업 +- 특성마다 다른 범위를 가지는 경우 머신러닝 모델들이 제대로 학습되지 않을 가능성이 있다. +(KNN, SVM, Neural network 모델, Clustering 모델 등) + + + +- 시력과 키를 함께 학습시킬 경우 키의 범위가 크기때문에 거리 값을 기반으로 학습 할 때 영향을 많이 준다. + +## 장점 + +- 특성들을 비교 분석하기 쉽게 만들어 준다. +- **Linear Model, Neural network Model** 등에서 학습의 안정성과 속도를 개선시킨다. +- 하지만 특성에 따라 원래 범위를 유지하는게 좋을 경우는 scaling을 하지 않아도 된다. + +## 종류 + + + +### StandardScaler + +- 변수의 **평균,표준편차를 이용**해 **정규분포 형태**로 변환 (평균 0, 분산 1) +- **이상치(Outlier)에 민감**하게 영향을 받는다 + +### RobustScaler + +- 변수의 **사분위수**를 이용해 변환 +- **이상치가 있는 데이터 변환**시 사용 할 수 있다. + +### MinMaxScaler + +- 변수의 **Max 값, Min 값을 이용**해 변환 (0 ~ 1 사이 값으로 변환) +- **이상치에 민감**하게 영향을 받는다. + +### Normalizer + +- **특성 벡터의 길이가 1**이 되도록 조정 (**행마다 정규화** 진행) +- 특성 벡터의 길이는 상관 없고 **데이터의 방향(각도)만 중요할 때** 사용. \ No newline at end of file diff --git "a/_posts/2022-09-20-ml03_Decision Tree(\352\262\260\354\240\225\355\212\270\353\246\254).md" "b/_posts/2022-09-20-ml03_Decision Tree(\352\262\260\354\240\225\355\212\270\353\246\254).md" new file mode 100644 index 000000000000..f055434083f5 --- /dev/null +++ "b/_posts/2022-09-20-ml03_Decision Tree(\352\262\260\354\240\225\355\212\270\353\246\254).md" @@ -0,0 +1,290 @@ +--- +layout: single +title: "Decision Tree 결정트리" +categories: 머신러닝 +tag: [머신러닝, python, Decision Tree, 결정트리, 분류] +toc: true +toc_sticky: true + +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 8px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + + .output_prompt { + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; + -webkit-overflow-scrolling: touch; + padding: 0.8rem; + margin-top: 0; + margin-bottom: 15px; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: $code-text-color; + border: solid 1px $border-color; + border-radius: 0.3rem; + word-break: normal; + white-space: pre; + } + + .dataframe tbody tr th:only-of-type { + vertical-align: middle; + } + + .dataframe tbody tr th { + vertical-align: top; + } + + .dataframe thead th { + text-align: center !important; + padding: 8px; + } + + .page__content p { + margin: 0 0 0px !important; + } + + .page__content p > strong { + font-size: 0.8rem !important; + } + + </style> +</head> + + +## Decision Tree란? + +- Tree를 만들기 위해 예/아니오 질문을 반복하며 학습한다. +- 다양한 **앙상블**(ensemble) 모델이 존재한다 (RandomForest, GradientBoosting, XGBoost, LightGBM) +- **분류**와 **회귀**에 모두 사용 가능 + + + +- 타깃 값이 한 개인 리프 노드를 순수 노드라고 한다. +- 모든 노드가 **순수 노드**가 될 때 까지 학습하면 **복잡해지고 과대적합**이 된다. + +## 결정 트리 과대적합 제어 + +- 노드 생성을 미리 중단하는 **사전 가지치기(pre-pruning)**와 트리를 만든후에 크기가 작은 노드를 삭제하는 **사후 가지치기(pruning)**가 있다 (sklearn은 사전 가지치기만 지원) +- 트리의 최대 깊이(**max_depth, 값이 클수록 모델의 복잡도가 올라간다**)나 리프 노드의 최대 개수(**max_leaf_nodes**)를 제어 +- 노드가 분할하기 위한 데이터 포인트의 최소 개수(**min_samples_leaf**)를 지정 + +## 지니 불순도 + + + +- 지니지수는 얼마나 불확실한가? (=**얼마나 많은 것들이 섞여있는가**?)를 보여준다. +- 지니 지수가 0이라는 것은 불확실성이 0이라는 것으로 **같은 특성을 가진 객체들끼리** 잘 모여있다는 의미이다. +- 지니 지수가 **0.5**(최대)라는 것은 **반반** 섞여 있다는 의미 + +# Decision Tree 실습 + +## 목표 + +- 독버섯과 식용버섯을 분리하는 tree 모델 만들기 +- tree 모델 시각화 +- tree 모델의 특성 중요도 확인 + +```python +import pandas as pd +import numpy as np +import matplotlib.pyplot as plt +import seaborn as sns +from sklearn.metrics import accuracy_score +from sklearn.model_selection import train_test_split +``` + +- 데이터 불러오기 + +```python +data = pd.read_csv('./data/mushroom/mushroom.csv') +display(data.shape) +display(data.head()) +``` + +``` +(8124, 23) +``` + +| | poisonous | cap-shape | cap-surface | cap-color | bruises | odor | gill-attachment | gill-spacing | gill-size | gill-color | … | stalk-surface-below-ring | stalk-color-above-ring | stalk-color-below-ring | veil-type | veil-color | ring-number | ring-type | spore-print-color | population | habitat | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| 0 | p | x | s | n | t | p | f | c | n | k | … | s | w | w | p | w | o | p | k | s | u | +| 1 | e | x | s | y | t | a | f | c | b | k | … | s | w | w | p | w | o | p | n | n | g | +| 2 | e | b | s | w | t | l | f | c | b | n | … | s | w | w | p | w | o | p | n | n | m | +| 3 | p | x | y | w | t | p | f | c | n | n | … | s | w | w | p | w | o | p | k | s | u | +| 4 | e | x | s | g | f | n | f | w | b | k | … | s | w | w | p | w | o | e | n | a | g | + +5 rows × 23 columns + +```python +data.info() +``` + +``` +<class 'pandas.core.frame.DataFrame'> +RangeIndex: 8124 entries, 0 to 8123 +Data columns (total 23 columns): + # Column Non-Null Count Dtype +--- ------ -------------- ----- + 0 poisonous 8124 non-null object + 1 cap-shape 8124 non-null object + 2 cap-surface 8124 non-null object + 3 cap-color 8124 non-null object + 4 bruises 8124 non-null object + 5 odor 8124 non-null object + 6 gill-attachment 8124 non-null object + 7 gill-spacing 8124 non-null object + 8 gill-size 8124 non-null object + 9 gill-color 8124 non-null object + 10 stalk-shape 8124 non-null object + 11 stalk-root 8124 non-null object + 12 stalk-surface-above-ring 8124 non-null object + 13 stalk-surface-below-ring 8124 non-null object + 14 stalk-color-above-ring 8124 non-null object + 15 stalk-color-below-ring 8124 non-null object + 16 veil-type 8124 non-null object + 17 veil-color 8124 non-null object + 18 ring-number 8124 non-null object + 19 ring-type 8124 non-null object + 20 spore-print-color 8124 non-null object + 21 population 8124 non-null object + 22 habitat 8124 non-null object +dtypes: object(23) +memory usage: 1.4+ MB +``` + +```python +X = data.iloc[:, 1:] +y = data['poisonous'] + +X.shape, y.shape +``` + +((8124, 22), (8124,)) + +- 데이터 전체를 원핫인코딩 + +```python +X_onehot = pd.get_dummies(X)X_onehot.head() +``` + +| | cap-shape_b | cap-shape_c | cap-shape_f | cap-shape_k | cap-shape_s | cap-shape_x | cap-surface_f | cap-surface_g | cap-surface_s | cap-surface_y | … | population_s | population_v | population_y | habitat_d | habitat_g | habitat_l | habitat_m | habitat_p | habitat_u | habitat_w | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | … | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | +| 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | … | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | +| 2 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | … | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | +| 3 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | … | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | +| 4 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | … | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | + +5 rows × 117 columns + +```python +X_train, X_test, y_train, y_test = train_test_split(X_onehot, y, test_size=0.3, random_state=926) + +X_train.shape, X_test.shape, y_train.shape, y_test.shape +``` + +((5686, 117), (2438, 117), (5686,), (2438,)) + +- 모델 생성 및 학습 + +```python +from sklearn.tree import DecisionTreeClassifier + +tree_model = DecisionTreeClassifier() +tree_model.fit(X_train, y_train) + +pred = tree_model.predict(X_test) +``` + +- 모델 평가 + +```python +accuracy_score(pred, y_test) +``` + +1.0 + +- 트리의 특성 중요도 확인 + - 트리 모델은 각 특성의 중요도를 확인 할 수 있음 + - **tree_model.feature_importances_** + +```python +# 데이터프레임화 하기, 중요도 기준으로 내림차순 정렬 +df = pd.DataFrame([X_train.columns, tree_model.feature_importances_]).T +df.sort_values(by=1, ascending=False) +``` + +| | 0 | 1 | +| --- | --- | --- | +| 27 | odor_n | 0.615161 | +| 53 | stalk-root_c | 0.16913 | +| 55 | stalk-root_r | 0.093209 | +| 100 | spore-print-color_r | 0.034151 | +| 33 | gill-spacing_c | 0.024172 | +| … | … | … | +| 39 | gill-color_g | 0.0 | +| 38 | gill-color_e | 0.0 | +| 37 | gill-color_b | 0.0 | +| 34 | gill-spacing_w | 0.0 | +| 116 | habitat_w | 0.0 | + +117 rows × 2 columns + +### Graphviz 시각화 + +- tree 모델 시각화 +- **Gini Impurity(지니 불순도)** 확인해보기 + +```python +from sklearn.tree import export_graphvizimport graphviz +``` + +```python +export_graphviz(tree_model, # 저장할 트리 모델 객체 + out_file='tree.dot', # 결과로 저장할 파일 + class_names=['독', '식용'], # 클래스 이름 설정 + feature_names=X_train.columns, # 컬럼 이름 넣어주기 + impurity=True, # 불순도 표기 여부 + filled=True, # 색상 채우기 여부 + rounded=True # 수치값 반올림 여부 + ) +``` + +```python +# 저장한 시각화 파일 불러오기 +with open('./tree.dot', encoding='utf-8') as f: + dot_graph = f.read() + +display(graphviz.Source(dot_graph)) +``` + + \ No newline at end of file diff --git "a/_posts/2022-09-24-ml04_\354\247\221\352\260\222 \354\230\210\354\270\241 DecisionTree(\355\232\214\352\267\200).md" "b/_posts/2022-09-24-ml04_\354\247\221\352\260\222 \354\230\210\354\270\241 DecisionTree(\355\232\214\352\267\200).md" new file mode 100644 index 000000000000..1a7319e99c34 --- /dev/null +++ "b/_posts/2022-09-24-ml04_\354\247\221\352\260\222 \354\230\210\354\270\241 DecisionTree(\355\232\214\352\267\200).md" @@ -0,0 +1,708 @@ +--- +layout: single +title: "Decision Tree 집값 예측(회귀)" +categories: 머신러닝 +tag: [머신러닝, python, Decision Tree, 결정트리, 회귀] +toc: true +toc_sticky: true + +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 8px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + + .output_prompt { + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; + -webkit-overflow-scrolling: touch; + padding: 0.8rem; + margin-top: 0; + margin-bottom: 15px; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: $code-text-color; + border: solid 1px $border-color; + border-radius: 0.3rem; + word-break: normal; + white-space: pre; + } + + .dataframe tbody tr th:only-of-type { + vertical-align: middle; + } + + .dataframe tbody tr th { + vertical-align: top; + } + + .dataframe thead th { + text-align: center !important; + padding: 8px; + } + + .page__content p { + margin: 0 0 0px !important; + } + + .page__content p > strong { + font-size: 0.8rem !important; + } + + </style> +</head> + + +## 목표 + +- Decision Tree 모델 사용하기 +- 모델 최적화하기 + +```python +# 예측하기 좋은 최적의 질문을 만들어 학습하는 모델 +from sklearn.tree import DecisionTreeRegressor + +house_model = DecisionTreeRegressor() +``` + +### 2. 모델 학습 + +- 데이터 로딩 후 탐색 + +```python +import pandas as pd + +# 데이터 로딩 +train = pd.read_csv('./data/house/train.csv') +test = pd.read_csv('./data/house/test.csv') +train.head() +``` + +| | Id | Suburb | Address | Rooms | Type | Method | SellerG | Date | Distance | Postcode | … | Car | Landsize | BuildingArea | YearBuilt | CouncilArea | Lattitude | Longtitude | Regionname | Propertycount | Price | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| 0 | 5467 | Rosanna | 22 Douglas St | 2 | h | S | Miles | 19/11/2016 | 11.4 | 3084 | … | 1.0 | 757 | NaN | NaN | Banyule | -37.74280 | 145.07000 | Eastern Metropolitan | 3540 | 1200000 | +| 1 | 4365 | North Melbourne | 103/25 Byron St | 1 | u | SP | Jellis | 16/07/2016 | 2.3 | 3051 | … | 1.0 | 0 | 60.0 | 2012.0 | Melbourne | -37.80200 | 144.95160 | Northern Metropolitan | 6821 | 450000 | +| 2 | 9741 | Surrey Hills | 4/40 Durham Rd | 3 | u | SP | Noel | 17/06/2017 | 10.2 | 3127 | … | 1.0 | 149 | NaN | NaN | Boroondara | -37.82971 | 145.09007 | Southern Metropolitan | 5457 | 780000 | +| 3 | 11945 | Cheltenham | 3/33 Sunray Av | 2 | t | S | Buxton | 29/07/2017 | 17.9 | 3192 | … | 1.0 | 171 | NaN | NaN | Kingston | -37.96304 | 145.06421 | Southern Metropolitan | 9758 | 751000 | +| 4 | 4038 | Mont Albert | 7/27 High St | 3 | t | S | Fletchers | 15/10/2016 | 11.8 | 3127 | … | 2.0 | 330 | 148.0 | 2001.0 | Whitehorse | -37.81670 | 145.10700 | Eastern Metropolitan | 2079 | 1310000 | + +5 rows × 22 columns + +```python +# 전체 데이터 갯수 파악 +train.shape, test.shape +``` + +((10185, 22), (3395, 21)) + +```python +# 컬럼 확인 +train.info() +``` + +``` +<class 'pandas.core.frame.DataFrame'> +RangeIndex: 10185 entries, 0 to 10184 +Data columns (total 22 columns): + # Column Non-Null Count Dtype +--- ------ -------------- ----- + 0 Id 10185 non-null int64 + 1 Suburb 10185 non-null object + 2 Address 10185 non-null object + 3 Rooms 10185 non-null int64 + 4 Type 10185 non-null object + 5 Method 10185 non-null object + 6 SellerG 10185 non-null object + 7 Date 10185 non-null object + 8 Distance 10185 non-null float64 + 9 Postcode 10185 non-null int64 + 10 Bedroom2 10185 non-null int64 + 11 Bathroom 10185 non-null int64 + 12 Car 10142 non-null float64 + 13 Landsize 10185 non-null int64 + 14 BuildingArea 5367 non-null float64 + 15 YearBuilt 6153 non-null float64 + 16 CouncilArea 9174 non-null object + 17 Lattitude 10185 non-null float64 + 18 Longtitude 10185 non-null float64 + 19 Regionname 10185 non-null object + 20 Propertycount 10185 non-null int64 + 21 Price 10185 non-null int64 +dtypes: float64(6), int64(8), object(8) +memory usage: 1.7+ MB +``` + +```python +# 기술 통계, include='all' 범주형 통계 값 포함(고유값, 최빈값, ...) +train.describe(include='all') +``` + +| | Id | Suburb | Address | Rooms | Type | Method | SellerG | Date | Distance | Postcode | … | Car | Landsize | BuildingArea | YearBuilt | CouncilArea | Lattitude | Longtitude | Regionname | Propertycount | Price | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| count | 10185.000000 | 10185 | 10185 | 10185.000000 | 10185 | 10185 | 10185 | 10185 | 10185.000000 | 10185.000000 | … | 10142.000000 | 10185.000000 | 5367.000000 | 6153.000000 | 9174 | 10185.000000 | 10185.000000 | 10185 | 10185.000000 | 1.018500e+04 | +| unique | NaN | 310 | 10066 | NaN | 3 | 5 | 243 | 58 | NaN | NaN | … | NaN | NaN | NaN | NaN | 33 | NaN | NaN | 8 | NaN | NaN | +| top | NaN | Reservoir | 2 Bruce St | NaN | h | S | Nelson | 27/05/2017 | NaN | NaN | … | NaN | NaN | NaN | NaN | Moreland | NaN | NaN | Southern Metropolitan | NaN | NaN | +| freq | NaN | 261 | 3 | NaN | 7106 | 6753 | 1156 | 364 | NaN | NaN | … | NaN | NaN | NaN | NaN | 887 | NaN | NaN | 3525 | NaN | NaN | +| mean | 6802.613942 | NaN | NaN | 2.943250 | NaN | NaN | NaN | NaN | 10.198213 | 3105.172607 | … | 1.613883 | 573.426411 | 154.137372 | 1964.904599 | NaN | -37.809763 | 144.995347 | NaN | 7447.172018 | 1.077961e+06 | +| std | 3926.702100 | NaN | NaN | 0.952794 | NaN | NaN | NaN | NaN | 5.866640 | 90.198740 | … | 0.959076 | 4550.757180 | 614.711880 | 37.603561 | NaN | 0.079922 | 0.104255 | NaN | 4354.473015 | 6.364301e+05 | +| min | 3.000000 | NaN | NaN | 1.000000 | NaN | NaN | NaN | NaN | 0.000000 | 3000.000000 | … | 0.000000 | 0.000000 | 0.000000 | 1196.000000 | NaN | -38.182550 | 144.431810 | NaN | 249.000000 | 1.310000e+05 | +| 25% | 3384.000000 | NaN | NaN | 2.000000 | NaN | NaN | NaN | NaN | 6.200000 | 3044.000000 | … | 1.000000 | 178.000000 | 93.920000 | 1940.000000 | NaN | -37.857700 | 144.929500 | NaN | 4380.000000 | 6.500000e+05 | +| 50% | 6838.000000 | NaN | NaN | 3.000000 | NaN | NaN | NaN | NaN | 9.300000 | 3084.000000 | … | 2.000000 | 448.000000 | 127.000000 | 1970.000000 | NaN | -37.802900 | 145.000130 | NaN | 6543.000000 | 9.050000e+05 | +| 75% | 10223.000000 | NaN | NaN | 4.000000 | NaN | NaN | NaN | NaN | 13.000000 | 3149.000000 | … | 2.000000 | 652.000000 | 175.000000 | 2000.000000 | NaN | -37.756710 | 145.059280 | NaN | 10331.000000 | 1.330000e+06 | +| max | 13577.000000 | NaN | NaN | 8.000000 | NaN | NaN | NaN | NaN | 48.100000 | 3977.000000 | … | 10.000000 | 433014.000000 | 44515.000000 | 2018.000000 | NaN | -37.408530 | 145.526350 | NaN | 21650.000000 | 7.650000e+06 | + +11 rows × 22 columns + +- 문제와 정답 추출 +- 일단은 결측치가 존재하는 컬럼과 문자형태의 컬럼은 배제 + +```python +X_train = train[['Propertycount', 'Rooms', 'Bedroom2']] +y_train = train['Price'] +``` + +```python +# train -> 7.5 : 2.5비율로 train2, validation 데이터로 나누기 +from sklearn.model_selection import train_test_split + +X_train2, X_val, y_train2, y_val = train_test_split(X_train, y_train, random_state=3) +X_train2.shape, X_val.shape, y_train2.shape, y_val.shape +``` + +((7638, 3), (2547, 3), (7638,), (2547,)) + +```python +house_model.fit(X_train2, y_train2) +``` + +DecisionTreeRegressor() + +### 3. 모델 예측 + +```python +pred = house_model.predict(X_val) +pred +``` + +array([ 451840. , 1344925. , 684462.5, …, 1843500. , 1300000. , 2272500. ]) + +### 4. 모델 평가 + +- MAE(Mean Absolute Error, 평균 절대값 오차) 활용한 평가 + +```python +from sklearn.metrics import mean_absolute_error +error = mean_absolute_error(y_val, pred) +error +``` + +255227.3455560717 + +### 캐글에 업로드 하기 + +```python +X_test = test[['Propertycount', 'Rooms', 'Bedroom2']] +X_test.shape +``` + +(3395, 3) + +```python +test_pre = house_model.predict(X_test) +test_pre +``` + +array([ 382333.33333333, 522500. , 700000. , …, 710500. , 651357.14285714, 1042205.88235294]) + +```python +# 정답지 파일 로딩 +submission = pd.read_csv("./data/house/sample_submission.csv") +submission +``` + +| | Id | Price | +| --- | --- | --- | +| 0 | 3189 | 0 | +| 1 | 2539 | 0 | +| 2 | 9171 | 0 | +| 3 | 4741 | 0 | +| 4 | 12455 | 0 | +| … | … | … | +| 3390 | 12276 | 0 | +| 3391 | 4618 | 0 | +| 3392 | 12913 | 0 | +| 3393 | 11741 | 0 | +| 3394 | 1072 | 0 | + +3395 rows × 2 columns + +```python +test.head() +``` + +| | Id | Suburb | Address | Rooms | Type | Method | SellerG | Date | Distance | Postcode | … | Bathroom | Car | Landsize | BuildingArea | YearBuilt | CouncilArea | Lattitude | Longtitude | Regionname | Propertycount | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| 0 | 3189 | Hawthorn | 22/9 Lisson Gr | 1 | u | S | Biggin | 19/11/2016 | 4.6 | 3122 | … | 1 | 1.0 | 0 | 52.0 | 1970.0 | Boroondara | -37.82610 | 145.02690 | Southern Metropolitan | 11308 | +| 1 | 2539 | Fitzroy | 113/300 Young St | 1 | u | SP | Jellis | 19/11/2016 | 1.6 | 3065 | … | 1 | 1.0 | 0 | 52.0 | 2011.0 | Yarra | -37.79740 | 144.97990 | Northern Metropolitan | 5825 | +| 2 | 9171 | Greenvale | 7 Murray Ct | 5 | h | S | Barry | 3/06/2017 | 20.4 | 3059 | … | 3 | 5.0 | 1750 | 310.0 | 1990.0 | Hume | -37.65439 | 144.89113 | Northern Metropolitan | 4864 | +| 3 | 4741 | Port Melbourne | 172 Albert St | 2 | h | S | hockingstuart | 10/12/2016 | 3.8 | 3207 | … | 1 | 0.0 | 106 | 70.0 | 1910.0 | Port Phillip | -37.83460 | 144.93730 | Southern Metropolitan | 8648 | +| 4 | 12455 | Brunswick West | 47 Everett St | 4 | h | VB | Nelson | 9/09/2017 | 5.2 | 3055 | … | 2 | 2.0 | 600 | 180.0 | 2004.0 | NaN | -37.75465 | 144.94144 | Northern Metropolitan | 7082 | + +5 rows × 21 columns + +```python +submission['Price'] = test_pre +submission.head() +``` + +| | Id | Price | +| --- | --- | --- | +| 0 | 3189 | 3.823333e+05 | +| 1 | 2539 | 5.225000e+05 | +| 2 | 9171 | 7.000000e+05 | +| 3 | 4741 | 9.572778e+05 | +| 4 | 12455 | 1.294818e+06 | + +```python +# csv파일로 저장 +submission.to_csv("./data/house/myPrediction.csv", + index=False) +``` + +## 다른 컬럼을 이용해보자. + +1. 결측치가 있는 컬럼 + - 데이터를 버린다. -> drop, dropna + - 데이터를 채운다. -> fillna + - 기술통계 활용 + - 모델 활용 -> 결측치를 정답, 주변컬럼을 문제로 설정 +2. 문자형태의 컬럼 + - 문자타입 -> 숫자타입 변경(인코딩) + - 라벨 인코딩 -> 임의의 숫자를 글자에 부여 + - 원핫 인코딩 -> 0과1을 이용해서 변환 + +```python +test.columns +``` + +Index([‘Id’, ‘Suburb’, ‘Address’, ‘Rooms’, ‘Type’, ‘Method’, ‘SellerG’, ‘Date’, ‘Distance’, ‘Postcode’, ‘Bedroom2’, ‘Bathroom’, ‘Car’, ‘Landsize’, ‘BuildingArea’, ‘YearBuilt’, ‘CouncilArea’, ‘Lattitude’, ‘Longtitude’, ‘Regionname’, ‘Propertycount’], dtype=‘object’) + +```python +train.corr() +``` + +| | Id | Rooms | Distance | Postcode | Bedroom2 | Bathroom | Car | Landsize | BuildingArea | YearBuilt | Lattitude | Longtitude | Propertycount | Price | h | t | u | Method_label | Regionname_label | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| Id | 1.000000 | 0.100741 | 0.293943 | 0.092843 | 0.116731 | 0.040834 | 0.099056 | 0.026959 | 0.024208 | 0.104456 | 0.040772 | 0.056133 | 0.008075 | -0.051498 | 0.114506 | -0.041921 | -0.099133 | 0.011529 | 0.267904 | +| Rooms | 0.100741 | 1.000000 | 0.294621 | 0.053791 | 0.937357 | 0.588991 | 0.406340 | 0.024508 | 0.115067 | -0.059154 | 0.012710 | 0.104277 | -0.081669 | 0.497539 | 0.507407 | -0.028028 | -0.543692 | 0.003428 | 0.166209 | +| Distance | 0.293943 | 0.294621 | 1.000000 | 0.430208 | 0.294438 | 0.120574 | 0.268467 | 0.024602 | 0.108834 | 0.245371 | -0.134113 | 0.239075 | -0.052206 | -0.164388 | 0.213490 | -0.012074 | -0.228571 | -0.093669 | 0.510509 | +| Postcode | 0.092843 | 0.053791 | 0.430208 | 1.000000 | 0.059840 | 0.108927 | 0.052475 | 0.027304 | 0.061235 | 0.021593 | -0.408562 | 0.450340 | 0.057250 | 0.111511 | -0.024155 | -0.008960 | 0.032696 | 0.016300 | -0.055349 | +| Bedroom2 | 0.116731 | 0.937357 | 0.294438 | 0.059840 | 1.000000 | 0.579122 | 0.402223 | 0.024382 | 0.112563 | -0.045148 | 0.012189 | 0.105248 | -0.080096 | 0.473742 | 0.485838 | -0.027327 | -0.520256 | 0.005557 | 0.165695 | +| Bathroom | 0.040834 | 0.588991 | 0.120574 | 0.108927 | 0.579122 | 1.000000 | 0.319449 | 0.038824 | 0.106673 | 0.154065 | -0.072667 | 0.116156 | -0.050214 | 0.464396 | 0.169435 | 0.119998 | -0.267174 | 0.084622 | 0.015280 | +| Car | 0.099056 | 0.406340 | 0.268467 | 0.052475 | 0.402223 | 0.319449 | 1.000000 | 0.023537 | 0.096233 | 0.113786 | -0.003670 | 0.067147 | -0.024338 | 0.235853 | 0.253054 | -0.014562 | -0.270764 | 0.012097 | 0.112987 | +| Landsize | 0.026959 | 0.024508 | 0.024602 | 0.027304 | 0.024382 | 0.038824 | 0.023537 | 1.000000 | 0.546089 | 0.040293 | 0.010407 | 0.010326 | -0.009704 | 0.040665 | 0.022618 | -0.018688 | -0.012693 | 0.029722 | 0.022445 | +| BuildingArea | 0.024208 | 0.115067 | 0.108834 | 0.061235 | 0.112563 | 0.106673 | 0.096233 | 0.546089 | 1.000000 | 0.021732 | 0.052281 | -0.033764 | -0.029216 | 0.082028 | 0.060088 | -0.004645 | -0.064177 | -0.001155 | 0.060186 | +| YearBuilt | 0.104456 | -0.059154 | 0.245371 | 0.021593 | -0.045148 | 0.154065 | 0.113786 | 0.040293 | 0.021732 | 1.000000 | 0.060937 | -0.017734 | 0.006838 | -0.324162 | -0.393885 | 0.298914 | 0.230626 | 0.027549 | 0.130729 | +| Lattitude | 0.040772 | 0.012710 | -0.134113 | -0.408562 | 0.012189 | -0.072667 | -0.003670 | 0.010407 | 0.052281 | 0.060937 | 1.000000 | -0.359648 | 0.060830 | -0.210428 | 0.108648 | -0.035545 | -0.096862 | -0.027790 | 0.141548 | +| Longtitude | 0.056133 | 0.104277 | 0.239075 | 0.450340 | 0.105248 | 0.116156 | 0.067147 | 0.010326 | -0.033764 | -0.017734 | -0.359648 | 1.000000 | 0.056610 | 0.206866 | -0.008365 | 0.006092 | 0.005237 | 0.022174 | -0.006846 | +| Propertycount | 0.008075 | -0.081669 | -0.052206 | 0.057250 | -0.080096 | -0.050214 | -0.024338 | -0.009704 | -0.029216 | 0.006838 | 0.060830 | 0.056610 | 1.000000 | -0.046582 | -0.065180 | -0.020591 | 0.085853 | -0.005943 | -0.184688 | +| Price | -0.051498 | 0.497539 | -0.164388 | 0.111511 | 0.473742 | 0.464396 | 0.235853 | 0.040665 | 0.082028 | -0.324162 | -0.210428 | 0.206866 | -0.046582 | 1.000000 | 0.391641 | -0.061995 | -0.392931 | 0.029182 | -0.237278 | +| h | 0.114506 | 0.507407 | 0.213490 | -0.024155 | 0.485838 | 0.169435 | 0.253054 | 0.022618 | 0.060088 | -0.393885 | 0.108648 | -0.008365 | -0.065180 | 0.391641 | 1.000000 | -0.453989 | -0.807573 | -0.065250 | 0.191063 | +| t | -0.041921 | -0.028028 | -0.012074 | -0.008960 | -0.027327 | 0.119998 | -0.014562 | -0.018688 | -0.004645 | 0.298914 | -0.035545 | 0.006092 | -0.020591 | -0.061995 | -0.453989 | 1.000000 | -0.158859 | 0.008709 | -0.037968 | +| u | -0.099133 | -0.543692 | -0.228571 | 0.032696 | -0.520256 | -0.267174 | -0.270764 | -0.012693 | -0.064177 | 0.230626 | -0.096862 | 0.005237 | 0.085853 | -0.392931 | -0.807573 | -0.158859 | 1.000000 | 0.066537 | -0.186580 | +| Method_label | 0.011529 | 0.003428 | -0.093669 | 0.016300 | 0.005557 | 0.084622 | 0.012097 | 0.029722 | -0.001155 | 0.027549 | -0.027790 | 0.022174 | -0.005943 | 0.029182 | -0.065250 | 0.008709 | 0.066537 | 1.000000 | -0.070270 | +| Regionname_label | 0.267904 | 0.166209 | 0.510509 | -0.055349 | 0.165695 | 0.015280 | 0.112987 | 0.022445 | 0.060186 | 0.130729 | 0.141548 | -0.006846 | -0.184688 | -0.237278 | 0.191063 | -0.037968 | -0.186580 | -0.070270 | 1.000000 | +- Type 컬럼 전처리 + +```python +# 원한인코딩 함수 +type_onehot = pd.get_dummies(train['Type']) +type_onehot +``` + +| | h | t | u | +| --- | --- | --- | --- | +| 0 | 1 | 0 | 0 | +| 1 | 0 | 0 | 1 | +| 2 | 0 | 0 | 1 | +| 3 | 0 | 1 | 0 | +| 4 | 0 | 1 | 0 | +| … | … | … | … | +| 10180 | 1 | 0 | 0 | +| 10181 | 1 | 0 | 0 | +| 10182 | 1 | 0 | 0 | +| 10183 | 1 | 0 | 0 | +| 10184 | 0 | 1 | 0 | + +10185 rows × 3 columns + +```python +type_onehot_test = pd.get_dummies(test['Type']) +type_onehot_test +``` + +| | h | t | u | +| --- | --- | --- | --- | +| 0 | 0 | 0 | 1 | +| 1 | 0 | 0 | 1 | +| 2 | 1 | 0 | 0 | +| 3 | 1 | 0 | 0 | +| 4 | 1 | 0 | 0 | +| … | … | … | … | +| 3390 | 1 | 0 | 0 | +| 3391 | 1 | 0 | 0 | +| 3392 | 0 | 1 | 0 | +| 3393 | 1 | 0 | 0 | +| 3394 | 0 | 0 | 1 | + +3395 rows × 3 columns + +```python +train = pd.concat([train, type_onehot], axis=1) +train.head() +``` + +| | Id | Suburb | Address | Rooms | Type | Method | SellerG | Date | Distance | Postcode | … | YearBuilt | CouncilArea | Lattitude | Longtitude | Regionname | Propertycount | Price | h | t | u | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| 0 | 5467 | Rosanna | 22 Douglas St | 2 | h | S | Miles | 19/11/2016 | 11.4 | 3084 | … | NaN | Banyule | -37.74280 | 145.07000 | Eastern Metropolitan | 3540 | 1200000 | 1 | 0 | 0 | +| 1 | 4365 | North Melbourne | 103/25 Byron St | 1 | u | SP | Jellis | 16/07/2016 | 2.3 | 3051 | … | 2012.0 | Melbourne | -37.80200 | 144.95160 | Northern Metropolitan | 6821 | 450000 | 0 | 0 | 1 | +| 2 | 9741 | Surrey Hills | 4/40 Durham Rd | 3 | u | SP | Noel | 17/06/2017 | 10.2 | 3127 | … | NaN | Boroondara | -37.82971 | 145.09007 | Southern Metropolitan | 5457 | 780000 | 0 | 0 | 1 | +| 3 | 11945 | Cheltenham | 3/33 Sunray Av | 2 | t | S | Buxton | 29/07/2017 | 17.9 | 3192 | … | NaN | Kingston | -37.96304 | 145.06421 | Southern Metropolitan | 9758 | 751000 | 0 | 1 | 0 | +| 4 | 4038 | Mont Albert | 7/27 High St | 3 | t | S | Fletchers | 15/10/2016 | 11.8 | 3127 | … | 2001.0 | Whitehorse | -37.81670 | 145.10700 | Eastern Metropolitan | 2079 | 1310000 | 0 | 1 | 0 | + +5 rows × 25 columns + +```python +test = pd.concat([test, type_onehot_test], axis=1) +test.head() +``` + +| | Id | Suburb | Address | Rooms | Type | Method | SellerG | Date | Distance | Postcode | … | BuildingArea | YearBuilt | CouncilArea | Lattitude | Longtitude | Regionname | Propertycount | h | t | u | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| 0 | 3189 | Hawthorn | 22/9 Lisson Gr | 1 | u | S | Biggin | 19/11/2016 | 4.6 | 3122 | … | 52.0 | 1970.0 | Boroondara | -37.82610 | 145.02690 | Southern Metropolitan | 11308 | 0 | 0 | 1 | +| 1 | 2539 | Fitzroy | 113/300 Young St | 1 | u | SP | Jellis | 19/11/2016 | 1.6 | 3065 | … | 52.0 | 2011.0 | Yarra | -37.79740 | 144.97990 | Northern Metropolitan | 5825 | 0 | 0 | 1 | +| 2 | 9171 | Greenvale | 7 Murray Ct | 5 | h | S | Barry | 3/06/2017 | 20.4 | 3059 | … | 310.0 | 1990.0 | Hume | -37.65439 | 144.89113 | Northern Metropolitan | 4864 | 1 | 0 | 0 | +| 3 | 4741 | Port Melbourne | 172 Albert St | 2 | h | S | hockingstuart | 10/12/2016 | 3.8 | 3207 | … | 70.0 | 1910.0 | Port Phillip | -37.83460 | 144.93730 | Southern Metropolitan | 8648 | 1 | 0 | 0 | +| 4 | 12455 | Brunswick West | 47 Everett St | 4 | h | VB | Nelson | 9/09/2017 | 5.2 | 3055 | … | 180.0 | 2004.0 | NaN | -37.75465 | 144.94144 | Northern Metropolitan | 7082 | 1 | 0 | 0 | + +5 rows × 24 columns + +- Method 컬럼 전처리 + +```python +# 라벨인코딩 +# 캐글의 metadata 정보 참고 +method_dict = { + 'S' : 0, + 'SP' : 1, + 'PI' : 2, + 'PN' : 3, + 'SN' : 4, + 'NB' : 5, + 'VB' : 6, + 'W' : 7, + 'SA' : 8, + 'SS' : 9 +} +method_label = train['Method'].map(method_dict) +method_label_test = test['Method'].map(method_dict) +method_label +``` + +``` +0 0 +1 1 +2 1 +3 0 +4 0 + .. +10180 0 +10181 2 +10182 0 +10183 2 +10184 0 +Name: Method, Length: 10185, dtype: int64 +``` + +```python +train['Method_label'] = method_label +test['Method_label'] = method_label_test +train.head() +``` + +| | Id | Suburb | Address | Rooms | Type | Method | SellerG | Date | Distance | Postcode | … | CouncilArea | Lattitude | Longtitude | Regionname | Propertycount | Price | h | t | u | Method_label | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| 0 | 5467 | Rosanna | 22 Douglas St | 2 | h | S | Miles | 19/11/2016 | 11.4 | 3084 | … | Banyule | -37.74280 | 145.07000 | Eastern Metropolitan | 3540 | 1200000 | 1 | 0 | 0 | 0 | +| 1 | 4365 | North Melbourne | 103/25 Byron St | 1 | u | SP | Jellis | 16/07/2016 | 2.3 | 3051 | … | Melbourne | -37.80200 | 144.95160 | Northern Metropolitan | 6821 | 450000 | 0 | 0 | 1 | 1 | +| 2 | 9741 | Surrey Hills | 4/40 Durham Rd | 3 | u | SP | Noel | 17/06/2017 | 10.2 | 3127 | … | Boroondara | -37.82971 | 145.09007 | Southern Metropolitan | 5457 | 780000 | 0 | 0 | 1 | 1 | +| 3 | 11945 | Cheltenham | 3/33 Sunray Av | 2 | t | S | Buxton | 29/07/2017 | 17.9 | 3192 | … | Kingston | -37.96304 | 145.06421 | Southern Metropolitan | 9758 | 751000 | 0 | 1 | 0 | 0 | +| 4 | 4038 | Mont Albert | 7/27 High St | 3 | t | S | Fletchers | 15/10/2016 | 11.8 | 3127 | … | Whitehorse | -37.81670 | 145.10700 | Eastern Metropolitan | 2079 | 1310000 | 0 | 1 | 0 | 0 | + +5 rows × 26 columns + +### 결측치와 문자형태를 처리해서 다양한 컬럼으로 학습해보자. + +1. train, test 데이터에 원하는 결측치,인코딩 처리 실시 +2. 원하는 컬럼 선택 +3. train을 train2와 val로 분리 +4. 모델 학습 후 평가 +5. test 데이터를 예측해 kaggle에 업로드 + +- Car 컬럼 전처리 + +```python +# Car 컬럼의 결측치 채우기 +train['Car'].describe() +``` + +``` +count 10142.000000 +mean 1.613883 +std 0.959076 +min 0.000000 +25% 1.000000 +50% 2.000000 +75% 2.000000 +max 10.000000 +Name: Car, dtype: float64 +``` + +```python +# 중앙값으로 채우기 +train['Car'] = train['Car'].fillna(train['Car'].median()) +test['Car'] = test['Car'].fillna(train['Car'].median()) +``` + +- Regionname 컬럼 전처리 + +```python +train['Regionname'].value_counts() +``` + +``` +Southern Metropolitan 3525 +Northern Metropolitan 2912 +Western Metropolitan 2219 +Eastern Metropolitan 1096 +South-Eastern Metropolitan 348 +Eastern Victoria 37 +Northern Victoria 30 +Western Victoria 18 +Name: Regionname, dtype: int64 +``` + +```python +regionname_dict = { + 'Southern Metropolitan' : 0, + 'Northern Metropolitan' : 1, + 'Western Metropolitan' : 2, + 'Eastern Metropolitan' : 3, + 'South-Eastern Metropolitan' : 4, + 'Eastern Victoria' : 5, + 'Northern Victoria' : 6, + 'Western Victoria' : 7 +} + +regionname_label = train['Regionname'].map(regionname_dict) +regionname_label_test = test['Regionname'].map(regionname_dict) +regionname_label +``` + +```python +train['Regionname_label'] = regionname_label +test['Regionname_label'] = regionname_label_test +train.head() +``` + +| | Id | Suburb | Address | Rooms | Type | Method | SellerG | Date | Distance | Postcode | … | Lattitude | Longtitude | Regionname | Propertycount | Price | h | t | u | Method_label | Regionname_label | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| 0 | 5467 | Rosanna | 22 Douglas St | 2 | h | S | Miles | 19/11/2016 | 11.4 | 3084 | … | -37.74280 | 145.07000 | Eastern Metropolitan | 3540 | 1200000 | 1 | 0 | 0 | 0 | 3 | +| 1 | 4365 | North Melbourne | 103/25 Byron St | 1 | u | SP | Jellis | 16/07/2016 | 2.3 | 3051 | … | -37.80200 | 144.95160 | Northern Metropolitan | 6821 | 450000 | 0 | 0 | 1 | 1 | 1 | +| 2 | 9741 | Surrey Hills | 4/40 Durham Rd | 3 | u | SP | Noel | 17/06/2017 | 10.2 | 3127 | … | -37.82971 | 145.09007 | Southern Metropolitan | 5457 | 780000 | 0 | 0 | 1 | 1 | 0 | +| 3 | 11945 | Cheltenham | 3/33 Sunray Av | 2 | t | S | Buxton | 29/07/2017 | 17.9 | 3192 | … | -37.96304 | 145.06421 | Southern Metropolitan | 9758 | 751000 | 0 | 1 | 0 | 0 | 0 | +| 4 | 4038 | Mont Albert | 7/27 High St | 3 | t | S | Fletchers | 15/10/2016 | 11.8 | 3127 | … | -37.81670 | 145.10700 | Eastern Metropolitan | 2079 | 1310000 | 0 | 1 | 0 | 0 | 3 | + +5 rows × 27 columns + +- CouncilArea 전처리 + +```python +X_train = train[['Rooms', 'Distance', 'Bedroom2', 'Bathroom', 'Car', 'Landsize', 'Propertycount', 'h', 't', 'u', 'Method_label', 'Regionname_label', 'Lattitude', 'Longtitude']] +y_train = train['Price'] + +X_test = test[['Rooms', 'Distance', 'Bedroom2', 'Bathroom', 'Car', 'Landsize', 'Propertycount', 'h', 't', 'u', 'Method_label', 'Regionname_label', 'Lattitude', 'Longtitude']] +X_train.head() +``` + +| | Rooms | Distance | Bedroom2 | Bathroom | Car | Landsize | Propertycount | h | t | u | Method_label | Regionname_label | Lattitude | Longtitude | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| 0 | 2 | 11.4 | 2 | 1 | 1.0 | 757 | 3540 | 1 | 0 | 0 | 0 | 3 | -37.74280 | 145.07000 | +| 1 | 1 | 2.3 | 1 | 1 | 1.0 | 0 | 6821 | 0 | 0 | 1 | 1 | 1 | -37.80200 | 144.95160 | +| 2 | 3 | 10.2 | 2 | 1 | 1.0 | 149 | 5457 | 0 | 0 | 1 | 1 | 0 | -37.82971 | 145.09007 | +| 3 | 2 | 17.9 | 2 | 1 | 1.0 | 171 | 9758 | 0 | 1 | 0 | 0 | 0 | -37.96304 | 145.06421 | +| 4 | 3 | 11.8 | 3 | 2 | 2.0 | 330 | 2079 | 0 | 1 | 0 | 0 | 3 | -37.81670 | 145.10700 | + +```python +X_test.head() +``` + +| | Rooms | Distance | Bedroom2 | Bathroom | Car | Landsize | Propertycount | h | t | u | Method_label | Regionname_label | Lattitude | Longtitude | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| 0 | 1 | 4.6 | 1 | 1 | 1.0 | 0 | 11308 | 0 | 0 | 1 | 0 | 0 | -37.82610 | 145.02690 | +| 1 | 1 | 1.6 | 1 | 1 | 1.0 | 0 | 5825 | 0 | 0 | 1 | 1 | 1 | -37.79740 | 144.97990 | +| 2 | 5 | 20.4 | 5 | 3 | 5.0 | 1750 | 4864 | 1 | 0 | 0 | 0 | 1 | -37.65439 | 144.89113 | +| 3 | 2 | 3.8 | 2 | 1 | 0.0 | 106 | 8648 | 1 | 0 | 0 | 0 | 0 | -37.83460 | 144.93730 | +| 4 | 4 | 5.2 | 4 | 2 | 2.0 | 600 | 7082 | 1 | 0 | 0 | 6 | 1 | -37.75465 | 144.94144 | +- 검증데이터 분리 + +```python +X_train3, X_val3, y_train3, y_val3 = train_test_split(X_train, y_train, test_size=0.2, random_state=916) +``` + +- 모델 정의 + +```python +house_model2 = DecisionTreeRegressor() +``` + +- 모델 학습 + +```python +house_model2.fit(X_train3, y_train3) +``` + +DecisionTreeRegressor() + +- 모델 예측 + +```python +pred = house_model2.predict(X_val3) +``` + +- 모델 평가 + +```python +mean_absolute_error(y_val3, pred) +``` + +245946.3264604811 + +- 캐글 제출 + +```python +submission['Price'] = house_model2.predict(X_test) +submission.head() +``` + +| | Id | Price | +| --- | --- | --- | +| 0 | 3189 | 348000.0 | +| 1 | 2539 | 597000.0 | +| 2 | 9171 | 725500.0 | +| 3 | 4741 | 1000000.0 | +| 4 | 12455 | 1610000.0 | + +```python +submission.to_csv("./data/house/myPrediction.csv", + index=False) +``` + +### 모델 최적화 + +- 모델복잡도 제어하기 +- KNN은 이웃의 숫자로 모델의 복잡도 제어 + - 이웃의 숫자가 커질수록 단순, + - 숫자가 적어질수록 복잡해진다. +- DecisionTree는 질문의 깊이로 모델의 복잡도를 제어(max_depth) + - 깊이가 얕으면 단순, + - 깊이가 깊으면 복잡해진다. + +```python +train_score_list = [] +val_score_list = [] + +for d in range(1, 30): + m = DecisionTreeRegressor(max_depth=d) + m.fit(X_train3, y_train3) + pred_train = m.predict(X_train3) + pred_val = m.predict(X_val3) + + score_train = mean_absolute_error(y_train3, pred_train) + score_val = mean_absolute_error(y_val3, pred_val) + train_score_list.append(score_train) + val_score_list.append(score_val) + print(d, ":", score_val) +``` + +``` +1 : 417733.49974970485 +2 : 343806.0797298899 +3 : 296665.1121222014 +4 : 266627.09369623766 +5 : 250658.9323204579 +6 : 240011.4545997507 +7 : 228006.44228511458 +8 : 225544.86401968033 +9 : 223158.24205112088 +10 : 222866.61170201903 +11 : 222247.53782971326 +12 : 226750.59009251397 +13 : 232785.61967598234 +14 : 236449.60651906623 +15 : 238339.72952208374 +16 : 235256.46193522654 +17 : 239032.74728354442 +18 : 246129.8931403534 +19 : 245102.87083875437 +20 : 248124.2068576584 +21 : 248081.48209490062 +22 : 247554.49879324844 +23 : 247564.5162821142 +24 : 243753.52724594992 +25 : 248604.58184694266 +26 : 244163.59204712813 +27 : 244919.02258222876 +28 : 245388.98429062346 +29 : 248771.09327442318 +``` + +```python +# 시각화 +import matplotlib.pyplot as plt + +plt.figure(figsize=(10, 5)) +plt.plot(range(1, 30), train_score_list) +plt.plot(range(1, 30), val_score_list) +plt.show() +``` + + + +- max_depth가 11일때 가장 낮은 MAE값을 얻을 수 있다. \ No newline at end of file diff --git "a/_posts/2022-09-29-ml05_Ensemble(\354\225\231\354\203\201\353\270\224).md" "b/_posts/2022-09-29-ml05_Ensemble(\354\225\231\354\203\201\353\270\224).md" new file mode 100644 index 000000000000..b80eab6e9071 --- /dev/null +++ "b/_posts/2022-09-29-ml05_Ensemble(\354\225\231\354\203\201\353\270\224).md" @@ -0,0 +1,155 @@ +--- +layout: single +title: "Ensemble(앙상블)" +categories: 머신러닝 +tag: [머신러닝, python, Ensemble, 결정트리, 앙상블] +toc: true +toc_sticky: true + +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 8px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + + .output_prompt { + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; + -webkit-overflow-scrolling: touch; + padding: 0.8rem; + margin-top: 0; + margin-bottom: 15px; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: $code-text-color; + border: solid 1px $border-color; + border-radius: 0.3rem; + word-break: normal; + white-space: pre; + } + + .dataframe tbody tr th:only-of-type { + vertical-align: middle; + } + + .dataframe tbody tr th { + vertical-align: top; + } + + .dataframe thead th { + text-align: center !important; + padding: 8px; + } + + .page__content p { + margin: 0 0 0px !important; + } + + .page__content p > strong { + font-size: 0.8rem !important; + } + + </style> +</head> + +## Decision Tree Ensemble(결정트리 앙상블) + +- 앙상블(ensemble)은 **여러 머신러닝 모델을 연결**하여 더 강력한 모델을 만드는 기법 +- 결정트리의 가지치기 후 **과대적합되는 단점을 보완**하는 모델 +- **RandomForest, GradientBoosting, XGBoost, LightGBM, CatBoost** 등 +- 회귀와 분류에 모두 사용 가능 + +## 앙상블 기법 + +1. **보팅(Voting)** + - 서로 다른 모델을 학습시켜 예측 결과를 **다수의 결과로 투표**하는 방법 + - 앙상블의 가장 기본 원리 + - **하드 보팅**(최종 예측 결과로만 보팅), **소프트 보팅**(모델들의 예측 확률로 보팅)으로 나뉜다. +2. **배깅(Bagging)** + - 같은 알고리즘을 가진 모델들을 학습시켜 예측 결과를 투표하는 방법 + - 학습 데이터(행)를 모델마다 다르게 샘플링(Bootstraping), 병렬 학습 + - 학습에 사용되는 컬럼(열)을 다르게 랜덤 선택 + - 배깅이 적용된 Decision Tree가 **RandomForest** +3. **부스팅** + - 같은 알고리즘을 가진 모델을 학습시켜 예측결과를 투표하는 방법 + - **이전 모델이 잘못 예측한 부분을 다음 모델이 강조해서 학습**하는 방법 + - 순차적 학습 -> 학습 속도가 비교적 느림 + +## RandomForest (배깅 모델) + +- 서로 다른 방향으로 **과대적합된 트리를 많이 만들고 평균을 내어 일반화** 시키는 모델 +- 다양한 트리를 만드는 방법 두 가지 + - 트리를 만들 때 사용하는 데이터 포인트 샘플을 무작위로 선택한다. + - 노드 구성시 기준이 되는 특성을 무작위로 선택하게 한다. +- **결정트리의 단점을 보완**하고 **장점은 그대로** 가지고 있는 모델이어서 별다른 조정 없이도 괜찮을 결과를 만들어낸다. +- 트리가 여러 개 만들어지기 때문에 비전문가에게 예측과정을 보여주기는 어렵다. +- 랜덤하게 만들어지기 때문에 random_state를 고정해야 같은 결과를 볼 수 있다. + +### 주요 매개변수 + +- n_estimators : 생성할 트리의 갯수 +- n개의 데이터 부트스트랩 샘플 구성 (n개의 데이터 포인트 중 무작위로 n 횟수만큼 반복 추출, 중복된 데이터가 들어 있을 수 있다.) +- max_features : 무작위로 선택될 후보 특성의 개수 (각 노드 별로 max_features 개수 만큼 무작위로 특성을 고른 뒤 최선의 특성을 찾는다.) +- max_features를 높이면 트리들이 비슷해진다. + +```python +from sklearn.ensemble import RandomForestClassifier + +rf_model = RandomForestClassifier(random_state=923, + n_estimators=1000, # 앙상블할 모델(tree)의 갯수 + max_features=0.6, # 학습에 사용할 특성(컬럼) 수 (설정된 값은 60%) + max_depth=15 # 트리의 깊이 설정 + ) +``` + +## GradientBoosting (부스팅 모델) + +- 정확도가 낮더라도 얕은 깊이의 모델을 만든 뒤, 나타난 예측 오류를 두 번째 모델이 보완한다. +- **이전 트리의 예측 오류를 보완하여 다음 트리를 만드는 작업을 반복**한다. +- 마지막까지 **성능을 쥐어짜고 싶은 경우** 사용한다, 주로 경진 대회에서 많이 활용. +(GradientBoosting을 더 발전시킨 **XGBoost**도 있음) +- 보통 트리의 깊이를 깊게하지 않기 때문에 **예측 속도는 비교적 빠르다**. 하지만 이전 트리의 오차를 반영해서 새로운 트리를 만들기 때문에 **학습속도가 느리다**. +- 특성의 **스케일을 조정하지 않아도 된다.** + +### 주요 매개변수 + +- n_estimators : 생성할 트리의 개수 (트리가 많아질수록 과대적합이 될 수 있다.) +- learning_rate : 오차를 보정하는 정도 (값이 높을수록 오차를 많이 보정하려고 한다.) +- max_depth : 트리의 깊이 (일반적으로 **트리의 깊이를 깊게 설정하지 않는다.**) + +```python +from sklearn.ensemble import GradientBoostingClassifier + +gbtree = GradientBoostingClassifier(n_estimators=100, # 트리의 갯수(순차적으로 생성되고, 이전 트리의 오류에 가중치가 더해짐) + learning_rate=0.1, # 이전트리의 오류를 다음트리에 얼마나 반영할지 + max_depth=5, # 트리의 깊이 + subsample=1 # 1: 전체데이터 사용, 과적합이 우려된다면 값을 줄여도 됨 + ) +``` \ No newline at end of file diff --git "a/_posts/2022-10-01-DL01_\352\262\275\354\202\254\355\225\230\352\260\225\353\262\225,\354\230\244\354\260\250\354\227\255\354\240\204\355\214\214,\355\231\234\354\204\261\355\231\224\355\225\250\354\210\230 \354\240\225\353\246\254.md" "b/_posts/2022-10-01-DL01_\352\262\275\354\202\254\355\225\230\352\260\225\353\262\225,\354\230\244\354\260\250\354\227\255\354\240\204\355\214\214,\355\231\234\354\204\261\355\231\224\355\225\250\354\210\230 \354\240\225\353\246\254.md" new file mode 100644 index 000000000000..b31027c1f61e --- /dev/null +++ "b/_posts/2022-10-01-DL01_\352\262\275\354\202\254\355\225\230\352\260\225\353\262\225,\354\230\244\354\260\250\354\227\255\354\240\204\355\214\214,\355\231\234\354\204\261\355\231\224\355\225\250\354\210\230 \354\240\225\353\246\254.md" @@ -0,0 +1,163 @@ +--- +layout: single +title: "경사하강법,오차역전파,활성화함수 정리" +categories: 딥러닝 +tag: [딥러닝, python, 경사하강법, 오차역전파, 활성화함수, 신경망] +toc: true +toc_sticky: true + +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 8px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + + .output_prompt { + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; + -webkit-overflow-scrolling: touch; + padding: 0.8rem; + margin-top: 0; + margin-bottom: 15px; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: $code-text-color; + border: solid 1px $border-color; + border-radius: 0.3rem; + word-break: normal; + white-space: pre; + } + + .dataframe tbody tr th:only-of-type { + vertical-align: middle; + } + + .dataframe tbody tr th { + vertical-align: top; + } + + .dataframe thead th { + text-align: center !important; + padding: 8px; + } + + .page__content p { + margin: 0 0 0px !important; + } + + .page__content p > strong { + font-size: 0.8rem !important; + } + + </style> +</head> + +## 오차 역전파 + +- 순전파 : 입력 데이터를 입력층에서부터 출력층까지 정방향으로 이동시키며 출력 값을 예측해 나가는 과정 +- 역전파 : 출력층에서 발생한 오차를 입력층 쪽으로 전파시키면서 최적의 결과를 학습해 나가는 과정 + + + +## 경사하강법 + +- 그 자리에서 loss가 작아지는 방향으로 이동하는 방법 +- 즉, 비용함수(loss)의 기울기를 구하여 기울기가 낮은 쪽으로 계속 이동하여 값을 최적화 시키는 방법이다. + + + +- 비용함수의 기울기 = loss 변화량 / w 변화량 + - w값의 변화량에 따라 loss가 변화하는 정도 + - 즉, 모델은 학습할 때, loss가 감소하는 w로 최적화한다. + - 최적화 함수(optimizer)가 경사하강법을 수행하면서 모델을 최적화한다. + +## 기울기 소실(Vanishing Gradient) + + + +- 깊은 층을 만들어 보니 출력층에서 시작된 가중치의 업데이트(역전파)가 처음 층까지 잘 전달되지 않는 현상 발생 +- 이를 기울기 소실이라고 함 +- 시그모이드 함수의 특성 때문 + +### sigmoid의 문제점 + +- 시그모이드 함수를 미분하면 최대치는 약 0.3임 +- 1보다 작기때문에 계속 곱하다 보면 0에 가까워져 가중치를 수정하기 어려워짐 + + + +## 활성화 함수 종류 + +- sigmoid의 문제점을 해결하기 위해 다양한 활성화 함수가 존재 + + + +## 최적화 함수(Optimizer)의 종류 + +### SGD(확률적 경사 하강법) + +- 경사하강법은 전체 데이터를 이용해 업데이트 하는 방법이다 +- 하지만, 실제로는 전체 데이터를 모두 활용하여 모델을 학습하기는 불가능(경사하강법은 이론상으로만 존재) +- SGD, 확률적 경사 하강법 : 확률적으로 선택된 일부 데이터를 이용해 업데이트 +- SGD를 수행할 때 확률적으로 선택할 데이터 갯수 : batch_size로 결정한다 + + + +### batch size + +- 일반적으로 PC 메모리의 한계 및 속도 저하 때문에 대부분의 경우 한번의 epoch에 모든 데이터를 집어넣기 힘듦 +- batch_size를 줄임 + - 메모리 소모가 적음 + - 학습 속도가 느림, 정확도는 높아짐 +- batch_size를 높임 + - 메모리 소모가 큼 + - 학습 속도가 빠름, 정확도는 낮아짐 +- batch_size의 디폴트 값은 32이며 일반적으로 32, 64가 많이 사용됨 + +### 모멘텀 + +- 경사 하강법에 관성을 적용해 업데이트함 +- 현재 batch 뿐만 아니라 이전 batch 데이터의 학습결과도 반영하는 방법 +- 개선된 모멘텀 방식으로는 네스테로프 모멘텀이 있다. + +### Adagrad(에이다그래드) + + + +- 학습을 진행하면서 학습률을 점차 줄여가는 방법 +- 처음에는 크게 학습하다가 조금씩 작게 학습함 +- 학습을 빠르고 정확하게 할 수 있는 장점이 있다. + +### 최적화 함수(Optimizer) 종류 한눈에 보기 + + + +- 방향(스텝 방향)과 학습률(스텝 사이즈)을 모두 고려하는게 Adam +- Adam을 사용하면 기본은 간다 \ No newline at end of file diff --git "a/_posts/2022-10-05-DL02_CNN \355\225\251\354\204\261\352\263\261 \354\213\240\352\262\275\353\247\235 \354\240\225\353\246\254.md" "b/_posts/2022-10-05-DL02_CNN \355\225\251\354\204\261\352\263\261 \354\213\240\352\262\275\353\247\235 \354\240\225\353\246\254.md" new file mode 100644 index 000000000000..98d519bd5821 --- /dev/null +++ "b/_posts/2022-10-05-DL02_CNN \355\225\251\354\204\261\352\263\261 \354\213\240\352\262\275\353\247\235 \354\240\225\353\246\254.md" @@ -0,0 +1,319 @@ +--- +layout: single +title: "Tensorflow:: CNN 합성곱 신경망 정리" +categories: 딥러닝 +tag: [딥러닝, python, 컨볼루젼, 합성곱, 신경망, 2D, 이미지 처리, tensorflow, CNN] +toc: true +toc_sticky: true + +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 8px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + + .output_prompt { + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; + -webkit-overflow-scrolling: touch; + padding: 0.8rem; + margin-top: 0; + margin-bottom: 15px; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: $code-text-color; + border: solid 1px $border-color; + border-radius: 0.3rem; + word-break: normal; + white-space: pre; + } + + .dataframe tbody tr th:only-of-type { + vertical-align: middle; + } + + .dataframe tbody tr th { + vertical-align: top; + } + + .dataframe thead th { + text-align: center !important; + padding: 8px; + } + + .page__content p { + margin: 0 0 0px !important; + } + + .page__content p > strong { + font-size: 0.8rem !important; + } + + </style> +</head> + +## MLP에서의 이미지 분석 + +- MLP 신경망을 이미지 처리에 사용하면 이미지의 위치에 민감하게 동작하여 **위치에 종속**적인 결과를 얻게 됨 +- 이는 모든 픽셀을 연산하기 때문 +- MLP로 이미지 처리를 하기 위해서는 이미지를 1차원 벡터로 변환하고 입력층으로 사용해야 하는데, 이러면 공간적인 구조 정보가 유실되므로 좀 더 정확한 예측이 힘들다. + +# CNN (Convolution Neural Network) + +- MLP에서는 크기가 바뀌거나 이미지 데이터의 위치, 회전 정도가 다를 경우 특징 추출이 굉장히 힘들다. +- 이러한 문제를 **CNN**으로 해결할 수 있다. + +합성곱 신경망은 이미지 처리에 탁월한 성능을 보이는 신경망이고, 특성추출부와 MLP로 이루어져 있다. + + + +## 특성추출부 + +- Convolution Layer(합성곱층) + + 특징들을 부각해주는 역할을 함 + +- Pooling Layer(풀링층) + + 특징이 약한 부분을 없애주는 역할 + + +**Conv Layer**는 특정 연산을 수행하기 때문에 뒤에 활성화 함수가 붙지만, **Pooling Layer**는 필요 없는 픽셀을 제거하는 역할만 수행하기 때문에 뒤에 활성화 함수가 오지 않는다. + +### 채널 + +- 합성곱 계층에서 이미지의 색상 정보를 채널이라고 부름 +- 이미지는 (높이, 너비, 채널)형태의 3차원 텐서 +- 흑백 이미지는 채널이 1, 컬러 이미지는(RGB) 3이다. + +## 필터(=커널 = 가중치) + +합성곱층은 합성곱 연산을 통해 이미지의 특징을 추출한다. + +이때, **필터**라는 N * M 크기의 행렬로 이미지를 처음부터 끝까지(가장 왼쪽 위 > 오른쪽 아래) 순차적으로 훑으면서(**슬라이딩**) 필터와 겹쳐지는 부분의 각 이미지와 필터의 원소(**가중치**) 값을 곱해서 모두 더한 값을 출력한다. 필터를 걸쳐 출력되는 데이터를 **특성맵**(feature map)이라고 한다. + + + +- 그림 설명 + + 채널이 1인 (흑백 이미지) 2차원 합성곱일 경우 입력층에 필터를 지나쳐 출력 되는 예시이다. + + **4x4**크기 Input 이미지와 **2x2**크기 kernel을 합성곱 연산할 경우 **3x3**형태의 특성맵이 출력됨. + + 물론, 필터에는 각 가중치가 존재하면서 **편향**도 존재한다. + + 예를 들어, 편향이 +1 이라면 출력되는 특성맵의 각 원소들은 +1이 되어 출력된다. + + +### 여러개의 필터 + +Dense 층을 여러개를 사용하는 것처럼 여러개의 필터를 사용할 수 있고, 각 필터의 가중치도 서로 다르다. + +여러개의 필터를 사용하면 특징 추출을 더 세밀하게 할 수 있다. + +- 채널 C개 입력 * 채널 C인 **필터가 FN개** = **채널 FN개 출력** +- 즉, **출력 데이터의 채널 수 = 필터의 개수** + + + +- 우선 Input 이미지는 크기가 4X4인 상태이다. + + 채널 1인 Input 이미지에 채널 1인 2X2 kernel 3개를 사용하면 3개의 3X3 크기의 특성맵을 얻을 수 있다. + + 즉, 한 이미지의 특징을 추출한 부분을 3개로 세분화 시켰고, 이미지의 크기는 줄어들었다. + + +### 3차원 합성곱 정리 + +보통 컬러 이미지를 입력 데이터로 사용한다면 3차원 배열(높이, 너비, 채널(깊이)) 형태이다. + +이러한 경우엔 커널도 동일한 채널을 갖게 된다. + +(**입력의 채널 수와 필터의 채널 수가 같아야함**) + +- **kernel의 채널(깊이)의 수 = Input 데이터의 채널(깊이)의 수** +- **합성곱 연산 결과** : Input 데이터는 **채널 수와 상관없이 kernel 별로 1개의 특성맵**이 만들어진다.즉, **입력 데이터의 깊이와 상관없이 (높이, 너비, 1)**의 특성 맵을 얻음 +- 다만, 합성곱 연산에서 다수의 커널을 사용할 경우 : 특성 맵은 **(높이, 너비, 커널의 수)** 크기 + + (특성 맵의 채널은 합성곱 연산에 사용된 커널의 수) + + + + +## 패딩(padding) + +합성곱 연산을 수행하며 kernel을 통과할때마다 특성맵의 크기는 점점 **줄어든다**. 하지만, 복잡한 학습을 위해 깊은 층을 쌓아야 한다면 크기가 점점 줄어드는 탓에 더 이상 연산이 힘들어지게 된다. + +그러므로, 여러 합성곱 연산 이후에도 특성맵의 크기를 비슷하게 유지되도록 **패딩**을 수행할 수 있다. + + + +- 입력 이미지의 가장 자리에 지정된 폭만큼 가상의 픽셀로 **테두리를 추가**한다. +- 그렇다면 필터의 **슬라이딩 범위가 늘어나게** 되고 kernel에서 출력되는 특성맵의 크기는 패딩하기 전보다 커지게 된다. +- 주로 0으로 채우기 대문에 제로 패딩(zero padding)이라고도 함 + +## 축소 샘플링 + +합성곱 수행 후 다음 계층으로 전달할 때, 모든 정보를 전달하지 않고 일부만 샘플링하여 넘겨주는 작업을 말한다. 크게 **스트라이드(stride)**와 **풀링(pooling)**이 있다. + +## 스트라이드(stride) + +그림에서는 kernel이 Input 이미지에 한 칸씩 이동(슬라이딩)하면서 합성곱 연산을 수행한다. + +하지만, 커널의 슬리이딩 범위 또한 사용자가 정할 수 있다. + +이러한 이동 범위를 **스트라이드(stride)**라고 한다. + +- kernel이 2픽셀. 3픽셀씩 건너 뛰면서 합성곱 연산을 수행하는 방법 +- stride2 또는 stride3이라고 하는데, 이렇게 하면 **ouput 특성맵의 크기를 더 줄일 수 있다.** + +## 풀링(pooling) + +일반적으로 합성곱 층(합성곱 연산 + 활성화 함수) 다음에는 풀링 층을 추가한다. + +합성곱 층 ouput인 특성맵이 크고 복잡하다면 이를 **축소(다운 샘플링)**해야 한다. 이 과정을 풀링 연산이라 하며, 이러한 연산을 하는 층이 **풀링 층**이다. + +- 일반적으로 **최대 풀링(max pooling)**과 **평균 풀링(average pooling)**이 사용된다. +- **최대 풀링**은 지역 내 최대 값을 뽑아내는 방법이다. +- 최대 풀링을 하면 지역 내의 대표 정보만 남기고 나머지 신호들을 제거하는 효과가 있음 +- **평균 풀링**은 지역 내 평균 값을 뽑아내는 방법이다. + + + +- 2X2 Max Pooling 수행 + + 4X4 특성맵을 2X2 맥스 풀링하면 절반으로 특성맵이 작아지는 것을 확인할 수 있다. + + 풀링은 통상적으로 2X2 풀링을 사용한다. + + +## 합성곱과 풀링의 차이 + +풀링도 합성곱 연산과 유사하게 커널을 슬라이딩 시켜 특성 맵을 얻으므로 유사한 느낌을 가질 수 있다. + +하지만, 풀링은 합성곱 연산과 달리 **곱하기나(가중치)** **더하는(편향)** 연산이 없고, 풀링 과정에서 커널이 슬라이딩할 때 서로 겹치지 않는다. + +## 합성곱층 + 풀링층 사용 + +앞서 확인한 내용들을 바탕으로 컨볼루션 층과 풀링 층은 쌍으로 보통 사용한다. + +(컨볼루션 층 + 풀링 층) 또는 (컨볼루션 층 + 컨볼루션 층 + 풀링 층) 이런 식으로 사용하게 되는데, + +2차원 이상의 크기로 출력됨 + +그 후에 밀집층(은닉층+출력층 또는 출력층)을 사용하여 원하는 결과값을 얻기 위해 1차원 배열로 풀어서 펼치는 작업 수행, 따라서 Flatten을 사용하여 1차원 크기로 바꾸어 사용한다. + +## CNN 모델 만들기 + +```python +from tensorflow.keras.layers import Conv2D, MaxPool2D, Flatten +import numpy as np +import pandas as pd +import matplotlib.pyplot as plt +``` + +- Conv2D : 2D 이미지 데이터에 대해 특징이 되는 부분들을 부각시켜줌 + - input_shape : Input 데이터 크기(height, width, channel) + - filters : 필터의 갯수 + - kernel_size : 필터의 크기 + - padding=‘same’ : 원본 데이터의 크기에 맞게 알아서 패딩 적용(valid : 적용X) +- MaxPool2D : 2D 이미지 데이터에 대해 필요 없는 정보를 삭제(축소 샘플링) + - pool_size : 디폴트 값은 2(2 X 2) + +```python +# 모델 설계 +cnn_model = Sequential() + +# 특성 추출부(Conv층) +cnn_model.add(Conv2D(input_shape=(224, 224, 3), filters=128, kernel_size=(3, 3), padding='same', activation='relu')) + +# 특성 추출부(Pooling층) +cnn_model.add(MaxPool2D()) +cnn_model.add(Conv2D(filters=256, kernel_size=(3, 3), padding='same', activation='relu')) +cnn_model.add(MaxPool2D()) +cnn_model.add(Conv2D(filters=128, kernel_size=(3, 3), padding='same', activation='relu')) +cnn_model.add(MaxPool2D()) +cnn_model.add(Conv2D(filters=64, kernel_size=(3, 3), padding='same', activation='relu'))cnn_model.add(MaxPool2D()) + +# 분류기(MLP) +cnn_model.add(Flatten()) +cnn_model.add(Dense(128, activation='relu')) +cnn_model.add(Dense(64, activation='relu')) +cnn_model.add(Dense(32, activation='relu')) +cnn_model.add(Dense(3, activation='softmax')) +``` + +- 생성한 CNN 모델 정보 확인하기 + +```python +cnn_model.summary() +``` + +``` +Model: "sequential_1" +_________________________________________________________________ + Layer (type) Output Shape Param # +================================================================= + conv2d (Conv2D) (None, 224, 224, 128) 3584 + + max_pooling2d (MaxPooling2D (None, 112, 112, 128) 0 + ) + + conv2d_1 (Conv2D) (None, 112, 112, 256) 295168 + + max_pooling2d_1 (MaxPooling (None, 56, 56, 256) 0 + 2D) + + conv2d_2 (Conv2D) (None, 56, 56, 128) 295040 + + max_pooling2d_2 (MaxPooling (None, 28, 28, 128) 0 + 2D) + + conv2d_3 (Conv2D) (None, 28, 28, 64) 73792 + + max_pooling2d_3 (MaxPooling (None, 14, 14, 64) 0 + 2D) + + flatten_1 (Flatten) (None, 12544) 0 + + dense_5 (Dense) (None, 128) 1605760 + + dense_6 (Dense) (None, 64) 8256 + + dense_7 (Dense) (None, 32) 2080 + + dense_8 (Dense) (None, 3) 99 + +================================================================= +Total params: 2,283,779 +Trainable params: 2,283,779 +Non-trainable params: 0 +_________________________________________________________________ +``` \ No newline at end of file diff --git "a/_posts/2022-10-09-springboot01_Spring Boot \355\224\204\353\241\234\354\240\235\355\212\270 \354\213\234\354\236\221.md" "b/_posts/2022-10-09-springboot01_Spring Boot \355\224\204\353\241\234\354\240\235\355\212\270 \354\213\234\354\236\221.md" new file mode 100644 index 000000000000..a42d2f4f0c85 --- /dev/null +++ "b/_posts/2022-10-09-springboot01_Spring Boot \355\224\204\353\241\234\354\240\235\355\212\270 \354\213\234\354\236\221.md" @@ -0,0 +1,282 @@ +--- +layout: single +title: "Spring Boot 프로젝트 시작" +categories: SpringBoot +tag: [Java, Spring, Spring Boot, STS, Eclipse] +toc: true +toc_sticky: true + +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 8px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + + .output_prompt { + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; + -webkit-overflow-scrolling: touch; + padding: 0.8rem; + margin-top: 0; + margin-bottom: 15px; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: $code-text-color; + border: solid 1px $border-color; + border-radius: 0.3rem; + word-break: normal; + white-space: pre; + } + + .dataframe tbody tr th:only-of-type { + vertical-align: middle; + } + + .dataframe tbody tr th { + vertical-align: top; + } + + .dataframe thead th { + text-align: center !important; + padding: 8px; + } + + .page__content p { + margin: 0 0 0px !important; + } + + .page__content p > strong { + font-size: 0.8rem !important; + } + + </style> +</head> + + +## STS로 스프링 부트 시작하기 + +- Java Development Kit(JDK): 8이상 (11이상 권장) +- STS 다운로드 : [https://spring.io/tools](https://spring.io/tools) + +## 프로젝트 생성하기 + +Spring Initializer로 프로젝트 세팅 후 STS에서 프로젝트를 시작하는 방법이다. + +[https://start.spring.io/](https://start.spring.io/) + + + +- 프로젝트 환경 + - Tool : STS + - build : Maven + - Language : Java + - Spring Boot Version : 2.7.4 + - Packageing : Jar + - JAVA Version : 8 + +이니셜라이저를 사용하게 되면 프로젝트를 좀 더 간편하게 생성할 수 있다. + +필요한 기능을 사용하기 위해 우측의 Dependencies에서 선택하여 프로젝트를 생성할 수 있기 때문에 초반 세팅에 귀찮은 작업에 낭비되는 시간을 절약할 수 있다. + +프로젝트 셋팅 완료 후 다운로드 받은 후, 해당 파일을 STS의 workspace로 이동시킨다. + +## Spring Boot 프로젝트 설정 + +## 1. porm.xml 설정 + +### **POM(프로젝트 객체 모델(Project Object Model))** + +- Maven의 기능을 이용하기 위해서 POM이 사용된다. +- POM은 pom.xml파일을 말하며 pom.xml은 Maven을 이용하는 프로젝트의 root에 존재하는 xml 파일이다. + + (하나의 자바 프로젝트에 빌드 툴을 Maven으로 설정하면, 프로젝트 최상위 디렉토리에 "pom.xml"이라는 파일이 생성된다.) + +- 파일은 프로젝트마다 1개이며, pom.xml만 보면 프로젝트의 모든 설정, 의존성 등을 알 수 있다. +- 다른 파일이름으로 지정할 수도 있다. (mvn -f 파일명.xml test). 하지만 pom.xml으로 사용하기를 권장 + +**ex) pom.xml 예시** + +```xml +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-parent</artifactId> + <version>2.7.4</version> + <relativePath/> <!-- lookup parent from repository --> + </parent> + <groupId>com.example</groupId> + <artifactId>demo</artifactId> + <version>0.0.1-SNAPSHOT</version> + <name>demo</name> + <description>Demo project for Spring Boot</description> + <properties> + <java.version>1.8</java.version> + </properties> + <dependencies> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-web</artifactId> + </dependency> + <dependency> + <groupId>org.mybatis.spring.boot</groupId> + <artifactId>mybatis-spring-boot-starter</artifactId> + <version>2.2.2</version> + </dependency> + + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-devtools</artifactId> + <scope>runtime</scope> + <optional>true</optional> + </dependency> + <dependency> + <groupId>mysql</groupId> + <artifactId>mysql-connector-java</artifactId> + <scope>runtime</scope> + </dependency> + <dependency> + <groupId>org.projectlombok</groupId> + <artifactId>lombok</artifactId> + <optional>true</optional> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-test</artifactId> + <scope>test</scope> + </dependency> + <!--ThymeLeaf--> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-thymeleaf</artifactId> + </dependency> + + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-maven-plugin</artifactId> + <configuration> + <excludes> + <exclude> + <groupId>org.projectlombok</groupId> + <artifactId>lombok</artifactId> + </exclude> + </excludes> + </configuration> + </plugin> + </plugins> + </build> + +</project> +``` + +### 엘리먼트 + +- modelVersion : POM model의 버전 +- parent : 프로젝트의 계층 정보 +- groupId : 프로젝트를 생성하는 조직의 고유 아이디를 결정한다. 일반적으로 도메인 이름을 거꾸로 적는다. +- artifactId : 프로젝트 빌드시 파일 대표이름 이다. groupId 내에서 유일해야 한다. Maven을 이용하여 빌드시 다음과 같은 규칙으로 파일이 생성 된다. + + artifactid-version.packaging. 위 예의 경우 빌드할 경우 bo-0.0.1-SNAPSHOT.war 파일이 생성된다. (하단 예시 파일 참고) + +- version : 프로젝트의 현재 버전, 프로젝트 개발 중일 때는 SNAPSHOT을 접미사로 사용. +- packaging : 패키징 유형(jar, war, ear 등) +- name : 프로젝트, 프로젝트 이름 +- description : 프로젝트에 대한 간략한 설명 +- url : 프로젝트에 대한 참고 Reference 사이트 +- properties : 버전관리시 용이 하다. ex) 하당 자바 버전을 선언 하고 dependencies에서 다음과 같이 활용 가능 하다. + + <version>${java.version}</version> + +- dependencies : dependencies태그 안에는 프로젝트와 의존 관계에 있는 라이브러리들을 관리 한다. +- build : 빌드에 사용할 플러그인 목록 + +### 라이브러리 + +Spring부트는 spring-boot-starter로 시작하는 라이브러리를 제공한다. starter-parent에 지정된 라이브러리 버전을 따른다. + +- spring-boot-starter-web + + : Spring MVC를 사용한 RESTful서비스를 개발하는데 사용. + +- spring-boot-starter-test + + : Junit, Hamcrest, Mockito를 포함하는 스프링 어플리케이션을 테스트 가능하도록 한다. + +- spring-boot-devtools + + : devtools는 Spring boot에서 제공하는 개발 편의를 위한 모듈이다. 쉽게 말하면 브라우저로 전송되는 내용들에 대한 코드가 변경되면, **자동으로 어플리케이션을 재시작**하여 브라우저에도 업데이트를 해주는 역할을 한다. + +- mybatis-spring-boot-starter + + : 스프링부트 위에 MyBatis 애플리케이션을 빠르게 빌드 할 수 있다. + + - DataSource 를 자동 감지합니다. + - SqlSessionFactory 를 전달 하는 인스턴스를 자동 생성하고 등록합니다 + - DataSource.SqlSessionFactoryBean 의 인스턴스를 만들고 등록합니다. + - @Mapper주석이 표시된 매퍼를 자동 스캔하고에 연결합니다. + - SqlSessionTemplateSpring 컨텍스트에 등록하여 Bean에 주입 할 수 있도록합니다. +- mysql-connector-java + + : 스프링부트에서 MySQL을 사용하기 위한 라이브러리 + +- lombok + + : Java 라이브러리로 반복되는 getter, setter, toString 등의 메서드 작성 코드를 줄여주는 코드 다이어트 라이브러리 + +- spring-boot-starter-thymeleaf + + : ThymeLeaf 템플릿을 사용하기 위한 라이브러리 + + +## 2. application.properties 설정 + +이 파일은 스프링부트가 애플리케이션을 구동할 때 자동으로 로딩하는 파일이다. + +key - value 형식으로 값을 정의하면 애플리케이션에서 참조하여 사용할 수 있다. + + + +- url을 호출할 때 필요한 context-path +- Server 포트 설정 + + 기본 포트는 8080 + +- JSP View Resolver(JSP 사용할 때) +- MySQL 접속 정보 +- mapper.xml 경로 설정(별도의 경로를 사용할 때 설정) +- ThymeLeaf 설정 + + 기본 경로는 classpath:/templates/ \ No newline at end of file diff --git "a/_posts/2022-10-10-DL03_\354\240\204\354\235\264\355\225\231\354\212\265 fine_tuning.md" "b/_posts/2022-10-10-DL03_\354\240\204\354\235\264\355\225\231\354\212\265 fine_tuning.md" new file mode 100644 index 000000000000..c3e891687584 --- /dev/null +++ "b/_posts/2022-10-10-DL03_\354\240\204\354\235\264\355\225\231\354\212\265 fine_tuning.md" @@ -0,0 +1,106 @@ +--- +layout: single +title: "Tensorflow:: 전이학습, Fine tuning" +categories: 딥러닝 +tag: [딥러닝, python, 전이학습, 사전학습, Fine tuning, tensorflow] +toc: true +toc_sticky: true + +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 8px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + + .output_prompt { + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; + -webkit-overflow-scrolling: touch; + padding: 0.8rem; + margin-top: 0; + margin-bottom: 15px; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: $code-text-color; + border: solid 1px $border-color; + border-radius: 0.3rem; + word-break: normal; + white-space: pre; + } + + .dataframe tbody tr th:only-of-type { + vertical-align: middle; + } + + .dataframe tbody tr th { + vertical-align: top; + } + + .dataframe thead th { + text-align: center !important; + padding: 8px; + } + + .page__content p { + margin: 0 0 0px !important; + } + + .page__content p > strong { + font-size: 0.8rem !important; + } + + </style> +</head> + +전이학습이란 다른 데이터 셋으로 이미 학습한 모델을 유사한 다른 데이터를 인식하는데 사용하는 기법 + +- 이 방법은 특히 새로 훈련시킬 데이터가 충분히 확보되지 못한 경우에 높은 학습 효율을 높여줌 +- 전이학습 모델을 적절히 이용하는 방법은 특성 추출(feature extraction) 방식과 미세 조정(fine-tuning) 방식이 있다. + +### 전이학습 - 특성 추출 방식(feature extraction) + + + +CNN 층에서 특성추출부만 가져와서 사용하는 방식이다. + +- 특성추출부 부분만 사용하는 이유는 분류기(MLP)의 경우 우리가 해결 하고자 하는 문제에 맞게 새로 설정해줘야 하기 때문 +- 단, 새롭게 분류할 클래스의 종류가 사전 학습에 사용된 데이터와 특징이 매우 다르면, 특성추출부 전체를 재사용해서는 안되고 앞 단의 일부 계층만을 재사용해야 함(심플한 특징들만 추출해내기 위해) + +### 전이학습 - 미세 조정 방식(fine-tuning) + + + +'사전 학습된 모델의 가중치’ 를 목적에 맞게 전체 또는 일부를 재학습시키는 방식이다. + +특성 추출부의 층들 중 하단부 몇 개의 계층을 전결합층 분류기(MLP)와 함께 새로 학습시킨다. + +- 처음부터 특성추출부 계층들과 분류기(MLP)를 같이 훈련시키면 새롭게 만든 분류기에서 발생하는 큰 에러 값으로 인해, 특성추출부에서 사전 학습된 가중치가 많이 손실될 수 있음 +- 처음에는 분류기(MLP)의 파라미터가 랜덤하게 초기화 되어 있으므로 컨볼루션 베이스 중 앞 단 계층들을 고정(동결)하고 뒷 단의 일부 계층만 학습이 가능하게 설정한 후, MLP와 같이 학습시켜 +파라미터(w, b) 들을 적당하게 잡아 준다. \ No newline at end of file diff --git "a/_posts/2022-10-14-DL04_\353\213\244\354\244\221\353\266\204\353\245\230_\353\252\250\353\215\270_\353\247\214\353\223\244\352\270\260(3\354\242\205\353\245\230_\353\217\231\353\254\274).md" "b/_posts/2022-10-14-DL04_\353\213\244\354\244\221\353\266\204\353\245\230_\353\252\250\353\215\270_\353\247\214\353\223\244\352\270\260(3\354\242\205\353\245\230_\353\217\231\353\254\274).md" new file mode 100644 index 000000000000..c344d3387c1b --- /dev/null +++ "b/_posts/2022-10-14-DL04_\353\213\244\354\244\221\353\266\204\353\245\230_\353\252\250\353\215\270_\353\247\214\353\223\244\352\270\260(3\354\242\205\353\245\230_\353\217\231\353\254\274).md" @@ -0,0 +1,688 @@ +--- +layout: single +title: "Tensorflow:: 다중분류_모델_만들기(3종류_동물)" +categories: 딥러닝 +tag: [딥러닝, python, 전이학습, 사전학습, Fine tuning, tensorflow, 다중분류, 이미지, CNN] +toc: true +toc_sticky: true + +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 8px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + + .output_prompt { + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; + -webkit-overflow-scrolling: touch; + padding: 0.8rem; + margin-top: 0; + margin-bottom: 15px; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: $code-text-color; + border: solid 1px $border-color; + border-radius: 0.3rem; + word-break: normal; + white-space: pre; + } + + .dataframe tbody tr th:only-of-type { + vertical-align: middle; + } + + .dataframe tbody tr th { + vertical-align: top; + } + + .dataframe thead th { + text-align: center !important; + padding: 8px; + } + + .page__content p { + margin: 0 0 0px !important; + } + + .page__content p > strong { + font-size: 0.8rem !important; + } + + </style> +</head> + +# 목표 + +1. CNN망 직접 설계하여 이미지 다중분류 하기(3개의 클래스) +2. VGG16모델 전이학습 활용하여 모델 보완 + 1. 전이학습 - 특성 추출 방식 + 2. 전이학습 - 미세 조정 방식 +3. 데이터 증식(ImageDataGenerator 사용) 활용하여 모델 보완 + +구글에 고슴도치, 햄스터, 푸들을 검색한 결과로 나온 이미지 각 400장을 크롤링하여 저장해 놓은 npz 데이터를 활용한다. + +저장된 npz 데이터를 활용하여 고슴도치, 햄스터, 푸들로 분류할 수 있는 CNN 모델 생성하기 + +## 생성한 NPZ 파일 로드 + +```python +data = np.load('/content/drive/MyDrive/Colab Notebooks/2022GJAI_DL/GJAI_DL/data/animal.npz') +``` + +```python +X_train = data['X_train'] +X_test = data['X_test'] +y_train = data['y_train'] +y_test = data['y_test'] + +X_train.shape, X_test.shape, y_train.shape, y_test.shape +``` + +``` +((960, 224, 224, 3), (240, 224, 224, 3), (960,), (240,)) +``` + +# 1. CNN 모델 만들기 + +```python +from tensorflow.keras.layers import Conv2D, MaxPool2D, Flatten +import numpy as np +import pandas as pd +import matplotlib.pyplot as plt +``` + +- **Conv2D** : 2D 이미지 데이터에 대해 특징이 되는 부분들을 부각시켜줌 + - input_shape : Input 데이터 크기(높이 ,넓이, 채널) + - filters : 필터의 갯수 + - kernel_size : 필터의 크기 + - padding=‘same’ : 원본 데이터의 크기에 맞게 알아서 패딩 적용(valid : 적용X) +- **MaxPool2D** : 2D 이미지 데이터에 대해 필요 없는 정보를 삭제(축소 샘플링) + - pool_size : 디폴트 값은 2(2 X 2) + +```python +# 모델 설계 +cnn_model = Sequential() + +# 특성 추출부(Conv층) +cnn_model.add(Conv2D(input_shape=(224, 224, 3), filters=128, kernel_size=(3, 3), padding='same', activation='relu')) + +# 특성 추출부(Pooling) +cnn_model.add(MaxPool2D()) +cnn_model.add(Conv2D(filters=256, kernel_size=(3, 3), padding='same', activation='relu')) +cnn_model.add(MaxPool2D()) +cnn_model.add(Conv2D(filters=128, kernel_size=(3, 3), padding='same', activation='relu')) +cnn_model.add(MaxPool2D()) +cnn_model.add(Conv2D(filters=64, kernel_size=(3, 3), padding='same', activation='relu'))cnn_model.add(MaxPool2D()) + +# 분류기(MLP) +cnn_model.add(Flatten()) +cnn_model.add(Dense(128, activation='relu')) +cnn_model.add(Dense(64, activation='relu')) +cnn_model.add(Dense(32, activation='relu')) +cnn_model.add(Dense(3, activation='softmax')) +``` + +### **생성한 CNN 모델 정보 확인하기** + +```python +cnn_model.summary() +``` + +``` +Model: "sequential_1" +_________________________________________________________________ + Layer (type) Output Shape Param # +================================================================= + conv2d (Conv2D) (None, 224, 224, 128) 3584 + + max_pooling2d (MaxPooling2D (None, 112, 112, 128) 0 + ) + + conv2d_1 (Conv2D) (None, 112, 112, 256) 295168 + + max_pooling2d_1 (MaxPooling (None, 56, 56, 256) 0 + 2D) + + conv2d_2 (Conv2D) (None, 56, 56, 128) 295040 + + max_pooling2d_2 (MaxPooling (None, 28, 28, 128) 0 + 2D) + + conv2d_3 (Conv2D) (None, 28, 28, 64) 73792 + + max_pooling2d_3 (MaxPooling (None, 14, 14, 64) 0 + 2D) + + flatten_1 (Flatten) (None, 12544) 0 + + dense_5 (Dense) (None, 128) 1605760 + + dense_6 (Dense) (None, 64) 8256 + + dense_7 (Dense) (None, 32) 2080 + + dense_8 (Dense) (None, 3) 99 + +================================================================= +Total params: 2,283,779 +Trainable params: 2,283,779 +Non-trainable params: 0 +_________________________________________________________________ +``` + +### **모델 컴파일** + +```python +cnn_model.compile(loss="sparse_categorical_crossentropy", + optimizer='Adam', + metrics=['acc'] + ) +``` + +### **모델 학습** + +```python +h1 = cnn_model.fit(X_train, y_train, validation_split=0.2, epochs=50, batch_size=128) +``` + +### **학습 과정 시각화** + +```python +plt.figure(figsize=(15, 5)) +plt.plot(h1.history['acc'], label='acc') +plt.plot(h1.history['val_acc'], label='val_acc') +plt.legend() +plt.show() +``` + + + +훈련 데이터에서는 정확도가 높지만, 검증셋에서 정확도가 매우 낮아지는 과적합 상태를 확인할 수 있다 + +### **정확도 외에 정밀도, 재현율, F1스코어 까지 확인해보기** + +- 모델의 예측결과는 각 클래스에 대한 확률값으로 나오기 때문에(softmax) +- 가장 높은 값의 인덱스를 반환해주는 np.argmax로 변환해서 점수 확인 + +```python +from sklearn.metrics import classification_report +pred = cnn_model.predict(X_test) +print(classification_report(y_test, np.argmax(pred, axis=1))) +``` + +``` +8/8 [==============================] - 2s 151ms/step + precision recall f1-score support + + 0 0.52 0.43 0.47 76 + 1 0.53 0.43 0.47 84 + 2 0.46 0.62 0.53 80 + + accuracy 0.50 240 + macro avg 0.50 0.50 0.49 240 +weighted avg 0.50 0.50 0.49 240 +``` + +- precision(정밀도) : 모델이 True라고 분류한것 중 실제 True의 비율 +- recall(재현율) : 실제 True인 것 중 모델이 True라고 예측한 비율 +- accuracy(정확도) : 전체 중에서 모델이 옳게 예측한 비율 +- f1-score : 정밀도와 재현율의 조화평균 + +정확도는 0.50으로 낮은 결과가 나왔다. + +즉, 직접 설계한 CNN 모델로는 한번에 좋은 결과를 도출하기 어려움 + +구글에서 크롤링한 데이터이기 때문에, 좋은 결과를 얻기 위해 VGG16모델을 이용해 전이학습을 해보자. + +# 2. VGG16 전이학습 수행 + +### VGG16 모델 불러오기 + +```python +from tensorflow.keras.applications import VGG16 +``` + +```python +pre_trained_model = VGG16(include_top=False, + weights='imagenet', + input_shape=(224, 224, 3) + ) +``` + +- include_top=False : 불러온 모델의 MLP층(분류기)을 사용하지 않고 특성추출부만 사용(=특성 추출 방식) +- weights=‘imagenet’ : 이미지넷 챌린지 대회에서 학습한 해당 모델의 w,b값을 그대로 사용(default) + +### VGG16 모델 정보 확인해보기 + +```python +pre_trained_model.summary() +``` + +``` +Model: "vgg16" +_________________________________________________________________ + Layer (type) Output Shape Param # +================================================================= + input_1 (InputLayer) [(None, 224, 224, 3)] 0 + + block1_conv1 (Conv2D) (None, 224, 224, 64) 1792 + + block1_conv2 (Conv2D) (None, 224, 224, 64) 36928 + + block1_pool (MaxPooling2D) (None, 112, 112, 64) 0 + + block2_conv1 (Conv2D) (None, 112, 112, 128) 73856 + + block2_conv2 (Conv2D) (None, 112, 112, 128) 147584 + + block2_pool (MaxPooling2D) (None, 56, 56, 128) 0 + + block3_conv1 (Conv2D) (None, 56, 56, 256) 295168 + + block3_conv2 (Conv2D) (None, 56, 56, 256) 590080 + + block3_conv3 (Conv2D) (None, 56, 56, 256) 590080 + + block3_pool (MaxPooling2D) (None, 28, 28, 256) 0 + + block4_conv1 (Conv2D) (None, 28, 28, 512) 1180160 + + block4_conv2 (Conv2D) (None, 28, 28, 512) 2359808 + + block4_conv3 (Conv2D) (None, 28, 28, 512) 2359808 + + block4_pool (MaxPooling2D) (None, 14, 14, 512) 0 + + block5_conv1 (Conv2D) (None, 14, 14, 512) 2359808 + + block5_conv2 (Conv2D) (None, 14, 14, 512) 2359808 + + block5_conv3 (Conv2D) (None, 14, 14, 512) 2359808 + + block5_pool (MaxPooling2D) (None, 7, 7, 512) 0 + +================================================================= +Total params: 14,714,688 +Trainable params: 14,714,688 +Non-trainable params: 0 +_________________________________________________________________ +``` + +## a. 전이학습 - 특성 추출 방식 이용 + +imagenet 챌린지 대회에서는 1000개의 이미지를 분류했으나 여기선 3개의 이미지를 분류할것이기 때문에 MLP층만 다르게 설정하는 특성 추출 방식으로 전이학습을 해보자 + +- VGG16의 특성추출부를 Sequential 객체에 추가하고, Flatten을 수행하여 직접 MLP를 정의해서 사용 +- 이전에 생성했던 CNN 모델과 비교를 하기 위해 은닉층의 형태와 컴파일 작업을 동일한 형태로 수행했다. + +```python +cnn_model2 = Sequential() + +# VGG16의 특성추출부 +cnn_model2.add(pre_trained_model) +# MLP +cnn_model2.add(Flatten()) +cnn_model2.add(Dense(128, activation='relu')) +cnn_model2.add(Dense(64, activation='relu')) +cnn_model2.add(Dense(32, activation='relu')) +cnn_model2.add(Dense(3, activation='softmax')) + +cnn_model2.compile(loss="sparse_categorical_crossentropy", + optimizer='Adam', + metrics=['acc'] + ) +``` + +```python +cnn_model2.compile(loss="sparse_categorical_crossentropy", + optimizer='Adam', + metrics=['acc'] + ) +``` + +```python +cnn_model2.fit(X_train, y_train, validation_split=0.2, epochs=50, batch_size=128) +``` + +### 정확도 확인해보기 + +정확도만 먼저 비교해보기 위해 과적합 여부를 확인하기 위한 시각화는 수행하지 않았다. + +```python +pred = cnn_model2.predict(X_test) +print(classification_report(y_test, np.argmax(pred, axis=1))) +``` + +``` +8/8 [==============================] - 3s 370ms/step + precision recall f1-score support + + 0 0.63 0.71 0.67 76 + 1 0.49 0.50 0.49 84 + 2 0.50 0.42 0.46 80 + + accuracy 0.54 240 + macro avg 0.54 0.55 0.54 240 +weighted avg 0.54 0.54 0.54 240 +``` + +직접 생성한 CNN모델보다 결과는 좀 더 좋아졌지만, 아직 많이 부족하기 때문에 미세 조정 방식을 사용해보자. + +## b. 전이학습 - 미세 조정 방식 이용 + +이번엔, VGG16 특성추출부의 맨 마지막 부분의 합성곱층을 재학습하는 작업을 추가 + +VGG16 모델을 다시 불러온다. + +```python +pre_trained_model2 = VGG16(include_top=False, + weights='imagenet', + input_shape=(224, 224, 3) + ) +``` + +```python +pre_trained_model2.summary() +``` + +### **VGG16 모델의 Layer 이름에 접근해보기** + +모든 VGG16의 layer 이름을 확인해본 출력값이다. + +```python +for layer in pre_trained_model2.layers: + print(layer.name) +``` + +``` +input_2 +block1_conv1 +block1_conv2 +block1_pool +block2_conv1 +block2_conv2 +block2_pool +block3_conv1 +block3_conv2 +block3_conv3 +block3_pool +block4_conv1 +block4_conv2 +block4_conv3 +block4_pool +block5_conv1 +block5_conv2 +block5_conv3 +block5_pool +``` + +맨 마지막 층인 block5_conv3 층만 재학습 시키고 나머지 Layer는 동결하기 + +### **layer.trainable 이용** + +```python +for layer in pre_trained_model2.layers: + if layer.name == 'block5_conv3': + layer.trainable = True + else: + layer.trainable = False +pre_trained_model2.summary() +``` + +``` +Model: "vgg16" +_________________________________________________________________ + Layer (type) Output Shape Param # +================================================================= + input_2 (InputLayer) [(None, 224, 224, 3)] 0 + + block1_conv1 (Conv2D) (None, 224, 224, 64) 1792 + + block1_conv2 (Conv2D) (None, 224, 224, 64) 36928 + + block1_pool (MaxPooling2D) (None, 112, 112, 64) 0 + + block2_conv1 (Conv2D) (None, 112, 112, 128) 73856 + + block2_conv2 (Conv2D) (None, 112, 112, 128) 147584 + + block2_pool (MaxPooling2D) (None, 56, 56, 128) 0 + + block3_conv1 (Conv2D) (None, 56, 56, 256) 295168 + + block3_conv2 (Conv2D) (None, 56, 56, 256) 590080 + + block3_conv3 (Conv2D) (None, 56, 56, 256) 590080 + + block3_pool (MaxPooling2D) (None, 28, 28, 256) 0 + + block4_conv1 (Conv2D) (None, 28, 28, 512) 1180160 + + block4_conv2 (Conv2D) (None, 28, 28, 512) 2359808 + + block4_conv3 (Conv2D) (None, 28, 28, 512) 2359808 + + block4_pool (MaxPooling2D) (None, 14, 14, 512) 0 + + block5_conv1 (Conv2D) (None, 14, 14, 512) 2359808 + + block5_conv2 (Conv2D) (None, 14, 14, 512) 2359808 + + block5_conv3 (Conv2D) (None, 14, 14, 512) 2359808 + + block5_pool (MaxPooling2D) (None, 7, 7, 512) 0 + +================================================================= +Total params: 14,714,688 +Trainable params: 2,359,808 +Non-trainable params: 12,354,880 +_________________________________________________________________ +``` + +summary 정보를 확인해보면 Trainable params에 block5_conv3의 param 갯수가 표시된다 + +즉, block5_conv3를 재학습 시킬 수 있다는 뜻이다. + +- Trainable params : 재학습 시킬 수 있는 파라미터 갯수 + +### **MLP 설계** + +이제 MLP(분류기)층을 설계해준다. 이전에 수행했던 특성 추출 방식과 비교해보기 위해 동일한 형태로 설정해준다. + +```python +cnn_model3 = Sequential() +# VGG16의 특성추출부 +cnn_model3.add(pre_trained_model2) +# MLP +cnn_model3.add(Flatten()) +cnn_model3.add(Dense(128, activation='relu')) +cnn_model3.add(Dense(64, activation='relu')) +cnn_model3.add(Dense(32, activation='relu')) +cnn_model3.add(Dense(3, activation='softmax')) +cnn_model3.summary() +``` + +``` +Model: "sequential_5" +_________________________________________________________________ + Layer (type) Output Shape Param # +================================================================= + vgg16 (Functional) (None, 7, 7, 512) 14714688 + + flatten_5 (Flatten) (None, 25088) 0 + + dense_21 (Dense) (None, 128) 3211392 + + dense_22 (Dense) (None, 64) 8256 + + dense_23 (Dense) (None, 32) 2080 + + dense_24 (Dense) (None, 3) 99 + +================================================================= +Total params: 17,936,515 +Trainable params: 5,581,635 +Non-trainable params: 12,354,880 +_________________________________________________________________ +``` + +- MLP를 직접 추가한 모델 + + Trainable params에 MLP 파라미터 갯수까지 추가된 걸 확인할 수 있다. + + 즉, 마지막 conv층 한개와 MLP(분류기)층을 다시 수행하고자 하는 목표에 맞게 재학습하게 될것이다. + + +### **모델 컴파일, 학습 및 결과** + +```python +cnn_model3.compile(loss="sparse_categorical_crossentropy", + optimizer='Adam', + metrics=['acc'] + ) +``` + +```python +cnn_model3.fit(X_train, y_train, validation_split=0.2, epochs=50, batch_size=128) +``` + +```python +pred = cnn_model3.predict(X_test) +print(classification_report(y_test, np.argmax(pred, axis=1))) +``` + +``` +8/8 [==============================] - 1s 155ms/step + precision recall f1-score support + + 0 0.85 0.79 0.82 76 + 1 0.80 0.86 0.83 84 + 2 0.91 0.90 0.91 80 + + accuracy 0.85 240 + macro avg 0.85 0.85 0.85 240 +weighted avg 0.85 0.85 0.85 240 +``` + +VGG16 모델을 미세 조정 방식으로 학습해본 결과 정확도는 0.85로 비교적 좋은 결과가 나왔다. + +즉, VGG16모델을 전이학습을 진행한 결과 직접 설계한 CNN모델에 비해 훨씬 결과가 잘 나오는 것을 확인할 수 있다. + +# 3. 데이터 증강(Data Augmantation) + +- 기존 이미지와 유사한 이미지를 추가로 생성하여 학습시 반영해주는 데이터 증식 기법 + +## ImageDataGenerator + +- 이미지 데이터를 생성하기 위한 조건을 설정할 수 있다. +- rotation_range : 이미지 회전 각도 설정 +- width_shift_range : 수평(x축) 이동 범위 설정 +- height_shift_range : 수직(y축) 이동 범위 설정 +- zoom_range : 축소/확대 비율 설정 +- horizontal_flip : 수평 방향으로 뒤집기 여부 설정 +- fill_mode : 이미지가 변형되면서 비는 공간에 어떻게 픽셀로 채워줄지(nearest: 가장 가까운 픽셀로 채우기) + +### **ImageDataGenerator 객체 생성** + +- 필요한 설정을 파라미터로 설정함 + +```python +from tensorflow.keras.preprocessing.image import ImageDataGeneratoraug = ImageDataGenerator(rotation_range=30, width_shift_range=0.2, # 20% 내외 수평이동 height_shift_range=0.2, # 20% 내외 수직이동 zoom_range=0.2, # 0.8 ~ 1.2배로 축소/확대 horizontal_flip=True, # 수평방향으로 뒤집기 fill_mode='nearest' ) +``` + +### **VGG16 모델 불러오기** + +- 목적에 맞게 학습시키기 위해 마지막 합성곱층 외에 동결시키기(전이학습 - 미세 조정 방식) +- VGG16의 마지막 합성곱층 이름은 block5_conv3로 확인할 수 있음 + +```python +pre_trained_model3 = VGG16(include_top=False, weights='imagenet', input_shape=(224, 224, 3) )for layer in pre_trained_model3.layers: if layer.name == 'block5_conv3': layer.trainable = True else: layer.trainable = False +``` + +``` +Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/vgg16/vgg16_weights_tf_dim_ordering_tf_kernels_notop.h5 +58889256/58889256 [==============================] - 0s 0us/step +``` + +### **MLP(분류기) 정의하기** + +- 목적에 맞게 입력층, 은닉층, 출력층 생성 +- 활성화함수 설정 + +```python +cnn_model4 = Sequential()# VGG16의 특성추출부cnn_model4.add(pre_trained_model3)# MLPcnn_model4.add(Flatten())cnn_model4.add(Dense(128, activation='relu'))cnn_model4.add(Dense(64, activation='relu'))cnn_model4.add(Dense(32, activation='relu'))cnn_model4.add(Dense(3, activation='softmax')) +``` + +### **모델 컴파일하기** + +```python +cnn_model4.compile(loss='sparse_categorical_crossentropy', optimizer='Adam', metrics=['acc']) +``` + +### **ImageDataGenerator로 설정한 조건으로 모델 학습** + +- ImageDataGenerator를 사용하여 학습하는 방법 + 1. .flow(x, y) + 2. .flow_from_directory(directory) +- aug.flow() : ImageDataGenerator로 설정한 조건으로 생성된 이미지로 학습에 적용시켜준다. +- steps_per_epoch = (훈련 샘플수 / 배치 사이즈) : 제너레이터로부터 얼마나 많은 샘플을 뽑을 것인지 결정 + - steps_per_epoch를 (X_train 길이 / batch_size) 즉, 7.5로 설정이 되면 한 epoch당 128개의 이미지를 활용하므로(batch_size때문에) 1epoch당 총 960개의 데이터를 활용하게 된다. +- 1epoch 때는 증강된 이미지 960개로 학습, 2epoch떄는 960개 추가되어 총 1920개로 학습 + +```python +cnn_model4.fit(aug.flow(X_train, y_train, batch_size=128), + steps_per_epoch=len(X_train) / 128, + epochs=50 + ) +``` + +```python +from sklearn.metrics import classification_report +pred = cnn_model4.predict(X_test) +print(classification_report(y_test, np.argmax(pred, axis=1))) +``` + +``` +8/8 [==============================] - 1s 156ms/step + precision recall f1-score support + + 0 0.80 0.93 0.86 76 + 1 0.96 0.79 0.86 84 + 2 0.89 0.91 0.90 80 + + accuracy 0.88 240 + macro avg 0.88 0.88 0.87 240 +weighted avg 0.88 0.88 0.87 240 +``` + +- ImageDataGenerator로 데이터 증식을 해서 학습하면 정확도가 약 88%로 더 나은 결과를 확인할 수 있음 +- 데이터의 수가 적을 경우 데이터 증강을 진행한 후 정확도가 더 향상됨 +- 데이터의 종류에 따라 정확도 개선의 정도는 다를 수 있음 \ No newline at end of file diff --git "a/_posts/2022-10-16-springboot02_Spring Boot \354\227\220\354\204\234 jsessionid \353\254\270\354\240\234 \355\225\264\352\262\260.md" "b/_posts/2022-10-16-springboot02_Spring Boot \354\227\220\354\204\234 jsessionid \353\254\270\354\240\234 \355\225\264\352\262\260.md" new file mode 100644 index 000000000000..d4a2e6d627a1 --- /dev/null +++ "b/_posts/2022-10-16-springboot02_Spring Boot \354\227\220\354\204\234 jsessionid \353\254\270\354\240\234 \355\225\264\352\262\260.md" @@ -0,0 +1,121 @@ +--- +layout: single +title: "Spring Boot 에서 jsessionid 문제 해결" +categories: SpringBoot +tag: [Java, Spring, Spring Boot, STS, Eclipse] +toc: true +toc_sticky: true + +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 8px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + + .output_prompt { + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; + -webkit-overflow-scrolling: touch; + padding: 0.8rem; + margin-top: 0; + margin-bottom: 15px; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: $code-text-color; + border: solid 1px $border-color; + border-radius: 0.3rem; + word-break: normal; + white-space: pre; + } + + .dataframe tbody tr th:only-of-type { + vertical-align: middle; + } + + .dataframe tbody tr th { + vertical-align: top; + } + + .dataframe thead th { + text-align: center !important; + padding: 8px; + } + + .page__content p { + margin: 0 0 0px !important; + } + + .page__content p > strong { + font-size: 0.8rem !important; + } + + </style> +</head> + + +## jsessionid란? + +- jsessionid는 새 세션이 만들어지면 클라이언트가 쿠키를 지원하는지 여부를 서버가 알 수 없으므로, 쿠키와 URL에 모두 jsessionid를 만들어 주는 것을 의미한다. +- url에 붙거나 헤더에 붙여서 표시된다. +- jsessionid를 탈취당하면 **사용자 ID, Password를 몰라도 접근이 가능하게 된다.** + +# 해결방법 + +### 1. application.properties에 아래와 같은 옵션을 추가한다. + + + +### 2. 클래스 계승 시작 SpringBootServletInitializer 재 작성 onStartup 방법 + +```java +@Override +public void onStartup(ServletContext servletContext) throws ServletException { + super.onStartup(servletContext); + servletContext.setSessionTrackingModes(Collections.singleton(SessionTrackingMode.COOKIE)); + SessionCookieConfig sessionCookieConfig=servletContext.getSessionCookieConfig(); + sessionCookieConfig.setHttpOnly(true); +} +``` + +### 3. @ Configuration 설정 클래스 에 bean 등록 + +ServletContextInitializer 클래스를 Bean객체로 등록하여 스프링 부트가 jseesion과 관련된 설정을 읽도록 해 주면 된다. + +```java +@Bean +public ServletContextInitializer servletContextInitializer1() { + return new ServletContextInitializer() { + @Override + public void onStartup(ServletContext servletContext) throws ServletException { + servletContext.setSessionTrackingModes(Collections.singleton(SessionTrackingMode.COOKIE) ); + } + }; +} +``` \ No newline at end of file diff --git "a/_posts/2022-10-20-DL05_\352\260\235\354\262\264 \355\203\220\354\247\200.md" "b/_posts/2022-10-20-DL05_\352\260\235\354\262\264 \355\203\220\354\247\200.md" new file mode 100644 index 000000000000..e1ec57589c19 --- /dev/null +++ "b/_posts/2022-10-20-DL05_\352\260\235\354\262\264 \355\203\220\354\247\200.md" @@ -0,0 +1,211 @@ +--- +layout: single +title: "객체 탐지 기술 정리" +categories: 딥러닝 +tag: [딥러닝, python, 객체탐지] +toc: true +toc_sticky: true + +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 8px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + + .output_prompt { + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; + -webkit-overflow-scrolling: touch; + padding: 0.8rem; + margin-top: 0; + margin-bottom: 15px; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: $code-text-color; + border: solid 1px $border-color; + border-radius: 0.3rem; + word-break: normal; + white-space: pre; + } + + .dataframe tbody tr th:only-of-type { + vertical-align: middle; + } + + .dataframe tbody tr th { + vertical-align: top; + } + + .dataframe thead th { + text-align: center !important; + padding: 8px; + } + + .page__content p { + margin: 0 0 0px !important; + } + + .page__content p > strong { + font-size: 0.8rem !important; + } + + </style> +</head> + +# 객체 탐지란 + +- 이미지 내에서 객체(사람, 사물 등)를 감지해 내는 것 + + + +# 평가지표 + +## IoU(Intersection over Union) + +- 이미지 내에 있는 **하나의 객체**를 탐지할 때 사용하는 평가지표이다. +- 실제 객체 면적과 모델이 예측한 면적의 **(교차 영역 / 전체 영역) X 100** +- 범위는 **0 ~ 1.0 사이** (일반적으로 **0.5**가 넘으면 맞게 예측했다고 판단) + + + +## mAP(mean Average Precision) + +- 한 이미지에서 **여러 개의 객체(클래스)**를 찾을 때 사용되는 평가지표이다. +- 각 객체(클래스)별 평균 정밀도(AP)를 계산한 후 **클래스 전체에 대한 평균(mean)**을 구하는 방식으로 범위값은 0 ~ 1.0 사이 +- IoU 기준에 따라 mAP는 달라진다(IoU가 높으면 mAP는 낮아짐) + +## 평균 정밀도(AP) + +- 일반적으로 정밀도와 재현율(Recall)은 **반비례** 관계이다. +- 두 값을 모두 고려하여 모델의 성능을 판단하는 것이 합리적이다. +- 재현율의 11개 지점(0.0 ~ 1.0)에 대해 정밀도를 구해서(11점 보간법) 평균을 낸 것이 AP이다. + +# 객체 탐지 알고리즘 + +## 1. Traditional Detection Methods + +- **슬라이딩 윈도우(Sliding Window)** + + 고정된 크기의 Window(=초록박스)로 이미지의 좌상단부터 우하단으로 **일일이 객체를 검출**해 나가는 방식 + +- **문제점** + + 객체가 없는 영역도 무조건 Sliding해야 하며 여러 Scale의 이미지를 스캔하여 검출하는 방식이므로 **수행시간은 늘고, 검출성능은 떨어진다.** + + +## 2. Two-stage detector + +- **영역추정(Region Proposal)**과 **탐지(Detection)** 두 단계를 따로 수행하는 방식 + + Sliding Window의 비효율성으로 인해 R-CNN 알고리즘에서는 **객체가 있을 법한 2000개의 영역**을 찾고 **그 영역에 대해서만 객체를 탐지**하는 두 단계를 제안함 + + 대표적으로는 R-CNN, Fast R-CNN, Faster R-CNN 모델이 있음 + +- **영역추정의 문제점** + + 객체들이 각기 다른 크기와 형태를 가지고 있다면 후보 영역을 찾는 정확도가 떨어지게 됨 + + 영역추정의 정확도를 향상시키기 위해 **미리 이미지에서 객체 영역을 분할**해 두면 후보 영역을 찾기가 더 쉽지 않을까 연구 진행 + +- **선택적 검색(Selective Search)** + + 영역추정의 문제점을 해결하기 위해 고안된 방법 + + 1. 처음에는 분할된 모든 부분들을 Bounding box로 만들어 리스트에 추가 + 2. 색상, 무늬, 크기, 형태에 따라 **유사도가 비슷한 부분들을 그룹핑**(Bbox 개수 감소) + 3. 1, 2단계를 계속 반복 + + +## 3. One-stage detector + +Two-stage detector는 Selective search 방식으로 인해 과거 대비 높은 정확도로 객체 탐지가 가능했지만, 여전히 낮은 속도로 **실시간 적용**은 어려웠음 + +One-stage detector는 **영역추정**과 **객체탐지**를 통합해 **한 번에 수행** + +가장 큰 장점은 탐지 속도의 획기적인 향상으로 **실시간 탐지가 가능**하다. + +# YOLO(You Only Look Once) + +- 대표적인 **One-stage detector** 방식 실시간 객체 검출 알고리즘 + - **객체 영역 판단**과 **객체 인식**을 동시에 진행하는 방식 + - 기존 CNN은 이미지 전체를 하나의 레이블(클래스)로 표시한다면, Yolo는 이미지 내에 특정한 위치(영역 추정)와 객체 탐지를 동시에 진행한 후, 이를 설정된 레이블로 표시해줌 +- 16년 **version1**부터 22년 **version7**까지 오픈소스로 출시됨 +- Yolo v1(GoogLeNet 적용)은 Two-stage detector의 Faster RCNN(vgg16 적용)보다 6배 빠른 속도로 논문에 기재됨 + +## YOLO(You Only Look Once) - v1 + + + +- 입력 이미지(448x448x3)를 7x7 Grid 영역으로 나눔 +- 각 Grid cell당 2개의 Bounding Box를 생성(총 98개) +- 각각의 Bbox는 **x, y, w, h**와 **confidence**로 구성됨 +- **x, y**는 Bbox의 중심점, **w, h**는 너비, 높이 +ex) x가 cell의 가장 왼쪽에 있다면 0이고 y가 cell의 중간에 있다면 0.5 +ex) 바운딩 박스의 w가 이미지 전체 너비의 절반이라면 w는 0.5 +- **Confidence**는 Bbox가 객체를 포함한다는 예측을 얼마나 확신 하는지에 대한 지표 + + + +- **Pr** : Grid Cell 내에 물체가 존재할 확률 (존재하면 1, 존재하지 않으면 0) +- confidence가 **0.5 이하**인 Bbox는 모두 삭제(0.5라는 기준은 사용자 지정 가능) +- 또한 confidence가 **가장 높은** Bbox만 남기고, 중복된다면 첫번째를 제외한 나머지 Bbox들은 삭제하여 **한 객체당 하나의 Bbox만 남김** + +## v2 + +- Bbox의 개수를 늘리고 GoogLeNet 대신 **Darknet-19** 모델을 사용하는 등 v1에 비해 **mAP 향상** +- Multi-Scaling기법을 사용하여 v1의 문제점인 **작은 객체에 대한 인식률 향상** + +## v3 + +- Darknet-53 모델로 변경하고 내부 구조를 조정하여 **FPS를 2배 이상 향상** +- 특성맵의 크기를 조절하여 크기가 **큰 객체의 검출 성능 향상** +- 다수의 객체 예측 시 softmax 대신 **개별 클래스별 sigmoid를 활용**하여 검출 + + (하나의 Bbox안에 복수의 객체가 존재하는 경우 softmax의 성능이 떨어짐) + + +## v4 + +- CSPDarknet53, SPP, PAN, BoF, Bos 등의 기법을 통해 v3에 비해 **mAP, FPS를 각각 10%, 12%씩 향상** + +## v5 ~ v6 + +- 논문 없이 깃 허브로 코드만 공유 +- V4에 비해 **낮은 용량, 빠른 속도(높은 FPS), 비슷한 성능(mAP)** +- Darknet 대신 **Pytorch**로 구현 +- 검출되는 객체의 **크기 별 전용 버전인 s, m, l, x**로 버전 세분화 +- Pascal VOC 데이터 셋 대신 **COCO 데이터 셋(20만개, 클래스 80개)**으로 훈련 + +## v7 + +- COCO 데이터 셋 기준 30FPS 이상의 실시간 감지에서 **AP 56.8%로 Yolo버전 중 가장 높음** +- 인간의 포즈를 추정할 수 있는 **포즈추정 모델 포함**(Yolo에서 첫 등장) + + \ No newline at end of file diff --git "a/_posts/2022-10-22-DL06_Yolov5 \354\213\244\354\212\265.md" "b/_posts/2022-10-22-DL06_Yolov5 \354\213\244\354\212\265.md" new file mode 100644 index 000000000000..0d1dc8f493e8 --- /dev/null +++ "b/_posts/2022-10-22-DL06_Yolov5 \354\213\244\354\212\265.md" @@ -0,0 +1,320 @@ +--- +layout: single +title: "Yolov5 파인 튜닝 실습" +categories: 딥러닝 +tag: [딥러닝, python, 객체탐지, Yolov5] +toc: true +toc_sticky: true + +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 8px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + + .output_prompt { + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; + -webkit-overflow-scrolling: touch; + padding: 0.8rem; + margin-top: 0; + margin-bottom: 15px; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: $code-text-color; + border: solid 1px $border-color; + border-radius: 0.3rem; + word-break: normal; + white-space: pre; + } + + .dataframe tbody tr th:only-of-type { + vertical-align: middle; + } + + .dataframe tbody tr th { + vertical-align: top; + } + + .dataframe thead th { + text-align: center !important; + padding: 8px; + } + + .page__content p { + margin: 0 0 0px !important; + } + + .page__content p > strong { + font-size: 0.8rem !important; + } + + </style> +</head> + +# 객체 탐지용 오픈소스 Yolov5 사용 + +- 객체 영역 판단과 객체 인식을 동시에 진행하는 **One-stage detector** 방식 +- 기존 CNN은 이미지 전체를 하나의 레이블(클래스)로 표시한다면, Yolo는 이미지 내에 특정한 위치(영역 추정)와 객체 탐지를 동시에 진행한 후, 이를 설정된 레이블로 표시해줌 + +## 1. 데이터 수집 + +- CNN처럼 이미지와 정답만 있으면 되는 것이 아니라 이미지 내에 정답이 될 객체들을 일일이 분리(라벨링)해서 정답으로 설정해줘야 함 +- roboflow 사이트에서 이미 라벨링 된 권총 데이터 사용 https://roboflow.com/ +- 실제 프로젝트에서 사용할 만한 라벨링 데이터는 ‘labelimg’ 라는 툴이 있음 + +```python +import numpy as np +import pandas as pd +import matplotlib.pyplot as plt +``` + +### 라벨링 된 권총 데이터 불러오기(from roboflow) + +- 코드로 권총 데이터셋을 다운로드하면, export 폴더(images, labels 존재)와 README파일 2개, data.yaml 파일이 다운로드 된다. +- 위 파일들을 roboflow_dataset이라는 디렉토리를 생성 후 이동시켜 줌 +- images + - 실제 학습 이미지 데이터 + - labels의 첫번째 0은 권총이라는 label(class)를 의미 +- labels + - 객체에 대한 정보들이 표시되어 있음 + - 나머지 값들은 Bbox의 x, y, w, h 값을 의미 + +```python +! curl -L "https://public.roboflow.com/ds/0wHxiSXST0?key=CMDIufmI48" > roboflow.zip; unzip roboflow.zip; rm roboflow.zip +``` + +### 환경설정 및 사전작업 + +1. 소스코드 가져오기 +- Yolov5 github url : https://github.com/ultralytics/yolov5 +- 위의 url을 복사, github clone으로 yolov5 소스코드 복제해오기 +- Google Drive의 /content 내에 yolov5 코드 복제함 + +```python +%cd /content + +!git clone https://github.com/ultralytics/yolov5 +``` + +1. yolov5 구동에 필요한 라이브러리 정의 +- yolov5 폴더 내에 requirements.txt에 필요한 라이브러리 정보가 정의되어 있음 +- 로컬 환경에서 작업하면 필요한 라이브러리 버전을 맞추는데에 어려움이 있어 Colab 환경에서 실행함 + +```python +# 해당 경로 파일의 모든 내용을 출력 +%cd /content/yolov5/ + +!pip install -r requirements.txt +``` + +1. data.yaml 파일 내용 확인 +- 데이터에 대한 정보가 담겨져 있는 파일로 yolo 학습시 사용 +- data.yaml 파일 내용 출력해보기 + +```python +%cat /content/roboflow_dataset/data.yaml +``` + +``` +train: ../train/images +val: ../valid/images + +nc: 1 +names: ['pistol'] +``` + +1. 이미지 데이터 불러오기 +- glob : 많은 양의 파일들의 처리를 쉽게 해주는 라이브러리 +- glob처리하면 img_list 변수에는 이미지 데이터들의 경로값이 담기게 된다. + +```python +from glob import glob + +img_list = glob('/content/roboflow_dataset/export/images/*.jpg') +len(img_list) +``` + +``` +2971 +``` + +- Train/Val 데이터 분리 + +```python +from sklearn.model_selection import train_test_split + +train_img_list, val_img_list = train_test_split(img_list, test_size=0.2, random_state=11) +len(train_img_list), len(val_img_list) +``` + +``` +(2376, 595) +``` + +- 데이터 경로들을 하나의 txt파일에 담아주기(train, val 별도) + +```python +# train +with open('/content/roboflow_dataset/train.txt', 'w') as f: + f.write('\n'.join(train_img_list) + '\n') +# val +with open('/content/roboflow_dataset/val.txt', 'w') as f: + f.write('\n'.join(val_img_list) + '\n') +``` + +1. data.yaml 파일의 train, val 경로를 맞게 재설정해주기 +- data.yaml 파일 읽어오기 + +```python +import yaml +with open('/content/roboflow_dataset/data.yaml', 'r') as f: + data = yaml.safe_load(f)data +``` + +``` +{'train': '../train/images', + 'val': '../valid/images', + 'nc': 1, + 'names': ['pistol']} +``` + +- data.yaml은 key, value 쌍의 딕셔너리 형태로 load됨 +- 경로 재설정하기 + +```python +data['train'] = '/content/roboflow_dataset/train.txt' +data['val'] = '/content/roboflow_dataset/val.txt'data +``` + +``` +{'train': '/content/roboflow_dataset/train.txt', + 'val': '/content/roboflow_dataset/val.txt', + 'nc': 1, + 'names': ['pistol']} +``` + +- yaml.dump로 data.yaml파일에서 data의 값들을 f에 덮어씌우기 + +```python +with open('/content/roboflow_dataset/data.yaml', 'w') as f: + yaml.dump(data, f) +``` + +### 모델 학습 + +- 모델을 학습시키는데 필요한 코드 파일인 train.py를 실행시킨다. + +### train.py 옵션 + +- –img : 입력 이미지의 크기, 해당 이미지 데이터의 크기는 416 X 416이다. +- –batch : 배치 사이즈 설정(데이터의 양이 많기 때문에 메모리를 고려하여 16정도까지만 높여줌) +- –data : data.yaml 파일 경로 설정 +- –cfg : yolo 모델의 세부 모델 설정(s, m, l, x 등) +- –weights : 기존 모델에 학습되어 있는 가중치 값을 그대로 사용(사용하는 모델의 가중치를 기입해야함, yolov5s.pt), 일부는 사용 목적에 맞게 재학습되도록 설정되어 있음 +- –name : 저장되는 결과 파일명 + +```python +%cd /content/yolov5 + +!python train.py --img 416 --batch 16 --epochs 50 --data /content/roboflow_dataset/data.yaml --cfg ./models/yolov5s.yaml --weights yolov5s.pt --name gun_yolov5s_results +``` + +### yolov5모델 학습 결과 해석 + + + + +<br> + + + +### 1. mAP50 + +- IoU = 0.5(실제와 예측이 50% 이상 겹쳤을 때 정답이라고 본다)일 때의 점수 + +### 2. mAP50-95 + +- IoU값을 0.5에서 0.05씩 증가시키며 0.95까지의 mAP값들의 평균값 + +보통 mAP50으로 모델의 성능을 판단하지만, 더 복잡해질 상황을 고려한다면 mAP50-95와 같이 판단, 고려해야함 + +## 학습 완료 후 평가지표 + +- 학습이 완료되면 모델 학습 설정에서 지정한 결과 파일명에 해당하는 디렉토리가 생성된다. +- 해당 폴더의 ./weights의 best.pt, last.pt + - best.pt : 학습 진행 중 가장 mAP가 높았을 때의 가중치가 저장됨 + - last.pt : 마지막 epoch에서의 가중치가 저장됨 + +### 평가지표 시각화하기 + +- 평가지표들을 tensorboard로 시각화해서 결과를 보기 쉽게 출력해보기 + +```python +%load_ext tensorboard +%tensorboard --logdir /content/yolov5/runs +``` + + + +## 모델 테스트 + +- 실제로 이미지를 넣어 제대로 검출되는지 확인해보기 +- 학습에 사용되지 않은 val 데이터로 확인 + +### 1. 검증용 데이터에서 임의로 한장 불러오기 + +```python +from IPython.display import Image + +val_img_path = val_img_list[8] +Image(val_img_path) +``` + + + +### 2. 학습된 yolo 모델에 객체 검출 요청하기 + +- detect.py 파일을 실행하여 모델에 요청한다 +- –weights 가중치 설정, 여기서는 best.pt(mAP가 가장 좋은 가중치)로 설정하였다. +- –conf : 0.5로 설정하였으므로 점수가 0.5보다 낮은 결과가 나오면 출력이 되지 않는다. + +```python +!python detect.py --weights /content/yolov5/runs/train/gun_yolov5s_results/weights/best.pt --img 416 --conf 0.5 --source "{val_img_path}" +``` + + + +- 결과 확인은 runs/detect/exp로 저장되었다고 나와있음 +- 여러번 하면 exp2, exp3, … 로 저장되게 된다. +- 결과 형태를 변경하고 싶거나, 다른 처리를 위해 핸들링하기 위해선 detect.py 파일 내의 코드를 수정하면 된다. + + + \ No newline at end of file diff --git a/_posts/2022-10-24-springboot03_Spring Data JPA.md b/_posts/2022-10-24-springboot03_Spring Data JPA.md new file mode 100644 index 000000000000..fbac83b23e5a --- /dev/null +++ b/_posts/2022-10-24-springboot03_Spring Data JPA.md @@ -0,0 +1,129 @@ +--- +layout: single +title: "Spring Data JPA란?" +categories: SpringBoot +tag: [Java, Spring, JPA, Spring Boot, STS, Eclipse] +toc: true +toc_sticky: true + +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 8px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + + .output_prompt { + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; + -webkit-overflow-scrolling: touch; + padding: 0.8rem; + margin-top: 0; + margin-bottom: 15px; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: $code-text-color; + border: solid 1px $border-color; + border-radius: 0.3rem; + word-break: normal; + white-space: pre; + } + + .dataframe tbody tr th:only-of-type { + vertical-align: middle; + } + + .dataframe tbody tr th { + vertical-align: top; + } + + .dataframe thead th { + text-align: center !important; + padding: 8px; + } + + .page__content p { + margin: 0 0 0px !important; + } + + .page__content p > strong { + font-size: 0.8rem !important; + } + + </style> +</head> + + + +## ORM이란? + +어플리케이션의 객체와 관계형 데이터베이스의 데이터를 자동으로 매핑해주는 것을 의미 + +- Java의 데이터 클래스와 관계형 데이터베이스의 테이블을 매핑 + +객체지향 프로그래밍과 관계형 데이터베이스의 차이로 발생하는 제약사항을 해결해주는 역할을 수행 + +대표적으로 JPA, Hibernate 등이 있음 (Persistent API) + +## ORM의 장점 + +1. SQL 쿼리가 아닌 직관적인 코드로 데이터를 조작할 수 있음 + - 개발자가 보다 비즈니스 로직에 집중할 수 있음 +2. 재사용 및 유지보수가 편리 + - ORM은 독릭접으로 작성되어 있어 재사용이 가능 + - 매핑정보를 명확하게 설계하기 때문에 따로 데이터베이스를 볼 필요가 없음 +3. DBMS에 대한 종속성이 줄어듬 + - DBMS를 교체하는 작업을 비교적 적은 리스크로 수행 가능 + +## ORM의 단점 + +1. 복잡성이 커질 경우 ORM만으로 구현하기 어려움 + - 직접 쿼리를 구현하지 않아 복잡한 설계가 어려움 +2. 잘못 구현할 경우 속도 저하 발생 +3. 대형 쿼리는 별도의 튜닝이 필요할 수 있음 + +## JPA (Java Persistance API) + +### **Hibernate** + +ORM Framework중 하나 + +JPA의 실제 구현체 중 하나이며, 현재 JPA 구현체 중 가장 많이 사용됨 + + + +### Spring Data JPA + +Spring Framework에서 JPA를 편리하게 사용할 수 있게 지원하는 라이브러리 + +- CRUD 처리용 인터페이스 제공 +- Repository 개발 시 인터페이스만 작성하면 구현 객체를 동적으로 생성해서 주입 +- 데이터 접근 계층 개발시 인터페이스만 작성해도 됨 + +Hibernate에서 자주 사용되는 기능을 조금 더 쉽게 사용할 수 있게 구현 \ No newline at end of file diff --git "a/_posts/2022-10-30-DL07_RNN \354\210\234\355\231\230 \354\213\240\352\262\275\353\247\235 \354\240\225\353\246\254.md" "b/_posts/2022-10-30-DL07_RNN \354\210\234\355\231\230 \354\213\240\352\262\275\353\247\235 \354\240\225\353\246\254.md" new file mode 100644 index 000000000000..90fb411a2345 --- /dev/null +++ "b/_posts/2022-10-30-DL07_RNN \354\210\234\355\231\230 \354\213\240\352\262\275\353\247\235 \354\240\225\353\246\254.md" @@ -0,0 +1,402 @@ +--- +layout: single +title: "RNN 순환 신경망 정리" +categories: 딥러닝 +tag: [딥러닝, python, 순환 신경망, RNN] +toc: true +toc_sticky: true + +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 8px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + + .output_prompt { + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; + -webkit-overflow-scrolling: touch; + padding: 0.8rem; + margin-top: 0; + margin-bottom: 15px; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: $code-text-color; + border: solid 1px $border-color; + border-radius: 0.3rem; + word-break: normal; + white-space: pre; + } + + .dataframe tbody tr th:only-of-type { + vertical-align: middle; + } + + .dataframe tbody tr th { + vertical-align: top; + } + + .dataframe thead th { + text-align: center !important; + padding: 8px; + } + + .page__content p { + margin: 0 0 0px !important; + } + + .page__content p > strong { + font-size: 0.8rem !important; + } + + </style> +</head> + +# RNN의 필요성 + +- 문장을 듣고 무엇을 의미하는지 알아야 하는 서비스 제공이 가능해진다. +- 문장을 듣고 이해한다는 것은 많은 문장을 이미 학습해 놓았다는 것이다. +- 문장의 의미를 전달하려면 각 단어가 **정해진 순서대로** 입력되어야 한다. +- **과거에 입력된 데이터와 나중에 입력된 데이터 사이의 관계를 고려**해야 하는 문제가 생긴다. +- **시간적 개념**이 들어간 데이터들을 해결하기 위해 **순환신경망(RNN)**이 고안되었다. + +# 순환신경망과 일반신경망의 차이 + +- RNN은 여러 개의 데이터가 순서대로 입력되었을 때 **앞서 입력 받은 데이터의 연산 결과를 잠시 기억**해 놓는 방식이다. +- 기억된 데이터를 가지고 다음 데이터로 넘어가면서 **함께 연산**한다. + + + +- **앞에서 나온 입력에 대한 결과가 뒤에서 나오는 입력 값에 영향**을 주는 것을 알 수 있다. +- 예를 들어, 비슷한 두 문장이 입력되어도 **앞에서 나온 입력 값을 구별**하여 출력 값에 반영할 수 있다. + + + +- 모든 입력 값에 이 작업을 순서대로 실행하므로 같은 층을 맴도는 것처럼 보인다. +- **같은 층안에서 맴도는 성질**때문에 순환 신경망이라고 부른다. + + + +### RNN 활용 데이터 종류 + +: 분석에 사용되는 특성들이 **시간적, 순차적 특징**을 지닌 데이터에 RNN 기법이 활용된다. + +- 시계열 데이터 +- 음악 데이터 +- 문장(자연어) 데이터 +- 번역 기술 등 + +### RNN 수식 살펴보기 + + + +- 지금까지 신경망에서는 대부분 높은 성능을 위해 활성화 함수로 relu를 사용하였다. +- 하지만, RNN에서는 이전 가중치를 기억해야하는데, relu는 0보다 작으면 0으로 만들어버리는 성질로 인해 이전 가중치에 대한 정보를 유지할 수 없다. +- 따라서, RNN에서는 -1 ~ 1 값을 가지는 하이퍼볼릭 탄젠트(tanh)를 사용한다. + +# RNN 기본 신경망(SimpleRNN) 코드 + +```python +SimpleRNN(units=3, imput_shape=(4, 9)) +``` + +- units : 퍼셉트론(뉴런의 개수) +- input_shape : (timesteps, features) 형태의 튜플로 들어감 + - timesteps : 순환 횟수 설정, 입력 시퀀스의 크기가 된다. + + +# RNN 데이터 구조 + + + +- features : 특성의 갯수 +- examples : 데이터 갯수 +- timesteps : 시간의 순서 + +# RNN 활용 구조 + +## 1. 다수 입력 단일 출력 + + + +- ex) **문장을 읽고 뜻을 파악**할 때 활용 가능 + +```python +model = Sequential() +model.add(SimpleRNN(units = output_size, + input_shape=(timesteps, features))) +``` + +## 2. 단일 입력 다수 출력 + + + +- ex) **사진의 캡션을 만들 때** 활용 가능 + +```python +model = Sequential() +model.add(RepeatVector(number_of_times, + input_shape=input_shape)) + +model.add(SimpleRNN(units = output_size, + return_sequences=True)) +``` + +※ number_of_times : 출력 개수 설정, ex) 3개의 캡션을 원하면 3으로 설정 + +※ return_sequences=True : SimpleRNN 신경망이 순환하며 단일 값을 계속 출력 + +## 3. 다수 입력 다수 출력 + + + +- ex) **문장을 번역**할 때 활용, **Video에서 Frame 단위 분류**에 활용 가능 +- RNN층을 여러개 쌓기 위해서 활용 가능 +- 여러 층으로 쌓기 위해서는 이전 RNN층이 **다수입력 다수출력 상태**가 되어야한다. +- 그래야 **다음 RNN층의 input 형태가 이전 RNN층과 같은 형태**가 되기 때문이다 + +```python +model = Sequential() +model.add(SimpleRNN(units = output_size, + input_shape=(timesteps, features), + return_sequences=True)) +``` + +# Simple RNN의 문제점 + +## 장기 의존성 문제(Long-Term Dependency) + +활성화 함수로 tanh를 사용하기 때문에 **timesteps(순환횟수)가 길어질수록 역전파시 기울기가 점차 줄어** 학습 능력이 저하된다 + +→ **기울기 소실 문제** 발생 + +→ 시간이 지나면 이전의 입력값을 잊어버리게 된다. + + + +# LSTM (Long Short Term Memory) + +: RNN의 문제점을 극복하기 위해 나온 대안 + +- 순환횟수가 많더라도 앞에서 연산한 결과를 **장기간 유지할 수 있는 ‘구조’**가 필요 +: RNN에 **메모리 셀(cell)** 추가 +- **장기기억**과 **단기기억**의 중요성을 계산해준다. +- 메모리 셀(cell) + - 시각 t에서 메모리 셀의 c에는 과거로부터 **현재시각 t까지의 필요한 대부분의 정보가 저장** + - 오차역전파 시 **tanh와 같은 활성화 함수를 통과하지 않아서 기울기 소실이 일어나지 않음** + - 데이터를 LSTM 계층 내에서만 주고 받으며 다른 계층으로는 전달하지 않음 + +## LSTM의 구조 + +- LSTM 1개는 3개의 gates**(forget, input, output)**로 구성 +- **forget gate**는 이전 상태 정보를 얼마나 버리고 얼마 만큼을 저장할지 결정하고, +- **Input gate**는 입력되는 새로운 정보를 얼마만큼 저장할지 결정하며, +- **output gate**는 현재 LSTM 셀의 어떤 부분을 다음 LSTM 셀로 전달할지를 결정 + + + +- **h는 단기 상태(Short-Term state)**를 **c는 장기 상태(Long-Term state)**라고 볼 수 있음 +- 이전 스텝의 장기 기억 ct -1은 왼쪽에서 오른쪽으로 통과하면서 Forget gate를 지나면서 일부 정보를 잃고(sigmoid가 곱해지므로), Input gate로부터 덧셈(+) 연산을 통해 새로운 정보를 추가하여 현재 타임 스텝의 장기 기억 ct 가 생성 됨 +- ct는 Output gate의 tanh 함수로 전달되어 단기 상태 ht를 만듦 + + + +- LSTM의 복잡성을 줄여 속도측면에서 더 빠른 효과를 볼 수 있는 **GRU**가 존재(목적에 맞는 속도에 따라 LSTM / GRU 선택) + +### 다중 LSTM Layer + +- LSTM층 또한, RNN과 마찬가지로 다중으로 쌓기 위해서는 이전 LSTM층이 **다수입력 다수출력 상태**가 되어야한다. +- 그래야 **다음 RNN층의 input 형태가 이전 RNN층과 같은 형태**가 되기 때문이다 +- LSTM, RNN층에서 순환할때마다 출력값을 만들어주는 **return_sequences=True** 옵션을 사용해야함 + +<br/> + +# 워드 임베딩(Word Embedding) + +- 컴퓨터가 자연어를 이해하고 효율적으로 처리하기 위해서는 **단순한 인코딩이 아닌 컴퓨터가 더 잘 이해할 수 있도록 변환** 할 필요가 있음 +- 워드 임베딩은 한 **단어의 의미를 풍부하게 만들어주는역할**(특성을 늘려주는 역할)을 함 +- 주로 **희소 표현(원 핫 인코딩)**에서 **밀집 표현(실수형태)**으로 변환하는 것을 의미 +- 밀집표현을 통해 해당 단어와 **유사한 다른 단어들의 수치(유사도)**까지 표시함 + + + +- 임베딩 과정을 통해 나온 결과를 **임베딩 벡터(embedding vector)**라고 함 +- 케라스에도 제공하는 도구인 **Embedding()**은 **랜덤한 값을 가지는 밀집 벡터로 변환**한 뒤에, 인공 신경망의 가중치를 학습하게 됨 + + + +### Embedding 사용 + +- **Embedding(사용하는 단어사전의 수, 한 단어를 표현할 특징의 수)**을 순환신경망층 가장 앞에 추가하기 +- 사용하는 단어 사전의 수는 **학습에 사용되는 단어의 갯수**이다 +- 한 단어를 표현할 특징의 수는 **적절한 수로 설정**해서 모델이 알아서 특징을 검출하게 해준다 + +# 순환 신경망 사용해보기 (SimpleRNN) + +## 1. 데이터 직접 준비 + +- 5개의 단어로 문자 하나하나를 단위로 하여 RNN을 사용해보기 +- “hello”, “apple”, “lobby”, “daddy”, “bobby” +- 문제 데이터 : “hell”, “appl”, “lobb”, “dadd”, “bobb” +- 정답 데이터 : “o”, “e”, “y”, “y”, “y” + +입력 데이터의 문자 갯수는 모두 4개이므로, **timesteps(순환 횟수)를 4**로 설정 + +### 학습을 위해 문자 데이터를 숫자 데이터로 인코딩하기 + +- RNN 데이터의 구조를 파악하기 위해 직접 one-hot 인코딩 수행 +- 문제, 정답 데이터에 등장하는 모든 문자(unique value)는 **h, e, l, o, a, p, b, y, d로 총 9개**이다. + - h : [1,0,0,0,0,0,0,0,0] + - e : [0,1,0,0,0,0,0,0,0] + - l : [0,0,1,0,0,0,0,0,0] + - o : [0,0,0,1,0,0,0,0,0] + - a : [0,0,0,0,1,0,0,0,0] + - p : [0,0,0,0,0,1,0,0,0] + - b : [0,0,0,0,0,0,1,0,0] + - y : [0,0,0,0,0,0,0,1,0] + - d : [0,0,0,0,0,0,0,0,1] + +```python +import numpy as np +import pandas as pd +import matplotlib.pyplot as plt + +# 문제 데이터 +X_train = np.array( + [ + + # 각각의 단어들 + [[1,0,0,0,0,0,0,0,0],[0,1,0,0,0,0,0,0,0],[0,0,1,0,0,0,0,0,0],[0,0,1,0,0,0,0,0,0]], # h, e, l, l + [[0,0,0,0,1,0,0,0,0],[0,0,0,0,0,1,0,0,0],[0,0,0,0,0,1,0,0,0],[0,0,1,0,0,0,0,0,0]], # a, p, p, l + [[0,0,1,0,0,0,0,0,0],[0,0,0,1,0,0,0,0,0],[0,0,0,0,0,0,1,0,0],[0,0,0,0,0,0,1,0,0]], # l, o, b, b + [[0,0,0,0,0,0,0,0,1],[0,0,0,0,1,0,0,0,0],[0,0,0,0,0,0,0,0,1],[0,0,0,0,0,0,0,0,1]], # d, a, d, d + [[0,0,0,0,0,0,1,0,0],[0,0,0,1,0,0,0,0,0],[0,0,0,0,0,0,1,0,0],[0,0,0,0,0,0,1,0,0]] # b, o, b, b + ]) + +# 정답 데이터 +y_train = np.array( + [ + [0,0,0,1,0,0,0,0,0], # o + [0,1,0,0,0,0,0,0,0], # e + [0,0,0,0,0,0,0,1,0], # y + [0,0,0,0,0,0,0,1,0], # y + [0,0,0,0,0,0,0,1,0] # y + + ]) + +X_train.shape, y_train.shape +``` + +``` +((5, 4, 9), (5, 9)) +``` + +### 데이터 구조 확인 + +- 문제 데이터의 크기 = (samples, timesteps, features) = (5, 4, 9) + - 5 : samples, 데이터의 갯수 + - 4 : timesteps, 순환 횟수 + - 9 : features, 특성 수(원핫 인코딩된 컬럼 수) +- 출력되는 정답을 알파벳 전체로 하고 싶다면 26개로 원핫 인코딩을 시켜주면 됨 +- 현재는 간단한 실습을 위해 9개의 문자로만 문제와 정답 설정 + +## 2. RNN 신경망 모델링 + +- SimpleRNN(units, input_shape=(timesteps, features)) + - units : 퍼셉트론(뉴런의 개수) + - input_shape : (timesteps, features) 형태의 튜플로 들어감 + +```python +from tensorflow.keras import Sequential +from tensorflow.keras.layers import Dense, SimpleRNN + +model = Sequential() +model.add(SimpleRNN(8, input_shape=(4, 9))) # 입력층 + 중간층 +model.add(Dense(9, activation='softmax')) # 출력층 +``` + +- 입력 시퀀스의 크기(순환 해야하는 횟수가 됨)는 4, 입력 데이터의 특성은 9 이므로 input_shapes = (4, 9) +- RNN층(중간층)에 사용되는 뉴런은 8개이다. +- 9개의 특성에서 정답을 맞춰야 하므로 출력층의 뉴런은 9이다. + +```python +model.summary() +``` + +``` +Model: "sequential_2" +_________________________________________________________________ + Layer (type) Output Shape Param # +================================================================= + simple_rnn_2 (SimpleRNN) (None, 8) 144 + + dense_2 (Dense) (None, 9) 81 + +================================================================= +Total params: 225 +Trainable params: 225 +Non-trainable params: 0 +_________________________________________________________________ +``` + +RNN은 가중치가 두 종류가 존재한다 (과거 데이터의 가중치, 현재 데이터의 가중치) + +- 현재 데이터의 가중치 + + → 9(입력 특성) X 8(RNN층 뉴런 수) + 8(RNN층 뉴런 수) = 80 + +- 과거 데이터의 가중치 + + → 8(RNN층 뉴런 수) X 8(RNN층 뉴런 수) = 64 + + - RNN층의 순환 횟수와는 상관없이 최종적으로 출력되는 연산 결과에만 가중치가 존재하므로 +- 파라미터 갯수 → 80 + 64 = 144 + +### 모델 컴파일 및 학습 + +```python +model.compile(loss='categorical_crossentropy', + optimizer='Adam', + metrics=['acc']) + +h = model.fit(X_train, y_train, epochs=300) +``` + +### 학습 현황 시각화 + +```python +plt.figure(figsize=(15, 5)) + +plt.plot(h.history['acc'], label='acc', marker='.') +plt.legend() +plt.show() +``` + + \ No newline at end of file diff --git "a/_posts/2022-11-05-DL08-1_RNN \355\231\234\354\232\251 \353\260\217 \354\213\244\354\212\265.md" "b/_posts/2022-11-05-DL08-1_RNN \355\231\234\354\232\251 \353\260\217 \354\213\244\354\212\265.md" new file mode 100644 index 000000000000..4a603e60c40c --- /dev/null +++ "b/_posts/2022-11-05-DL08-1_RNN \355\231\234\354\232\251 \353\260\217 \354\213\244\354\212\265.md" @@ -0,0 +1,387 @@ +--- +layout: single +title: "RNN 실습, 뉴스 기사 분류" +categories: 딥러닝 +tag: [딥러닝, python, 순환 신경망, RNN, 다중분류] +toc: true +toc_sticky: true + +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 8px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + + .output_prompt { + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; + -webkit-overflow-scrolling: touch; + padding: 0.8rem; + margin-top: 0; + margin-bottom: 15px; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: $code-text-color; + border: solid 1px $border-color; + border-radius: 0.3rem; + word-break: normal; + white-space: pre; + } + + .dataframe tbody tr th:only-of-type { + vertical-align: middle; + } + + .dataframe tbody tr th { + vertical-align: top; + } + + .dataframe thead th { + text-align: center !important; + padding: 8px; + } + + .page__content p { + margin: 0 0 0px !important; + } + + .page__content p > strong { + font-size: 0.8rem !important; + } + + </style> +</head> + +# RNN을 활용한 로터스 뉴스 분류 + +- 영국의 뉴스 통신사 로이터의 ’기사 내용’이 들어가면 어떤 ’주제’인지 분류하는 RNN 모델 실습 + +## keras에서 제공하는 데이터셋 불러오기 + +- 실제 뉴스 기사의 내용이 이미 단어단위로 토큰화, 라벨 인코딩 되어 저장이 된 상태이다. +- 단어가 많은 경우 원핫인코딩 상태로 저장을 한다면, 저장공간 측면에서 매우 비효율적임 +- 또한, 텍스트 처리 분야에서는 문제 데이터에 라벨 인코딩을 활용하면 단어의 빈도수까지 고려해줄 수 있음 +- 1: 뉴스의 시작을 알리는 인덱스 번호 +- 2 : oov(Out of Vocabulary)로 사용 +- 3 ~ : 뉴스 전체 내용에서 빈도수 기준으로 랭크를 나타낸다 + +```python +import numpy as np +import pandas as pd +import matplotlib.pyplot as plt +from tensorflow.keras.datasets import reuters + +data = reuters.load_data() +(X_train, y_train), (X_test, y_test) = data + +X_train.shape, y_train.shape, X_test.shape, y_test.shape +``` + +``` +((8982,), (8982,), (2246,), (2246,)) +``` + +0번째 훈련 데이터 에는 87개의 단어가 포함되어 있다. + +```python +len(X_train[0]) +``` + +``` +87 +``` + +### 로이터 뉴스 기사의 단어들이 어떤 숫자로 인코딩 되어 있는지 확인 + +1. reuters.get_word_index()로 확인이 가능 +- key, value 쌍의 딕셔너리 형태 + +```python +news_words = reuters.get_word_index() +news_words +``` + +``` +{'mdbl': 10996, + 'fawc': 16260, + 'degussa': 12089, + 'woods': 8803, + 'hanging': 13796, + ...} +``` + +1. value값 기준으로 news_words 정렬하기 + +```python +sorted(news_words.items(), key=lambda x : x[1]) +``` + +``` +[('the', 1), + ('of', 2), + ('to', 3), + ('in', 4), + ('said', 5), + ...] +``` + +1. 기사가 어떤 단어로 구성되어 있는지 단어들을 이어붙여 확인해보기 +- key값과 value 값의 위치를 교체 + +```python +word_of_news = {} + +for k, v in news_words.items(): + word_of_news[v] = k +``` + +- 단어 이어 붙이기 + +```python +print(' '.join([word_of_news[w] for w in X_train[0]])) +``` + +``` +the wattie nondiscriminatory mln loss for plc said at only ended said commonwealth could 1 traders now april 0 a after said from 1985 and from foreign 000 april 0 prices its account year a but in this mln home an states earlier and rise and revs vs 000 its 16 vs 000 a but 3 psbr oils several and shareholders and dividend vs 000 its all 4 vs 000 1 mln agreed largely april 0 are 2 states will billion total and against 000 pct dlrs +``` + +## 뉴스 주제의 개수를 알아보기 + +- 뉴스 카테고리 갯수 : 46가지 + +```python +np.unique(y_train) +``` + +``` +array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45]) +``` + +## 데이터 가공 + +- timestpes를 설정해야 하는데 각 문제 데이터(X_train)가 포함하는 단어의 갯수는 모두 다르다. +- 그러므로, 문제 데이터의 **단어 개수를 같게(입력 시퀀스 맞춰주기)** 맞춰줘야 학습이 가능하다. +- 즉, **긴 기사는 잘라내고, 짧은 기사는 붙여 넣는 작업**이 필요하다. + +### 시퀀스 길이 맞추기 + +몇번 순환 시킬지(timesteps) 고정 시켜주기 위함 - 각 문제 데이터의 길이들을 불러와서 기술 통계값 확인해보기 + +```python +# 각 문제 데이터의 길이 저장 +train_len = [len(x) for x in X_train] + +# 기술 통계값 확인 +print(f'최댓값 : {max(train_len)}') +print(f'최솟값 : {min(train_len)}') +print(f'평균값 : {np.mean(train_len)}') +print(f'중앙값 : {np.median(train_len)}') +``` + +``` +최댓값 : 2376 +최솟값 : 13 +평균값 : 145.5398574927633 +중앙값 : 95.0 +``` + +- 히스토그램으로 데이터 밀도 구성 확인 +- x축은 뉴스 길이를 각 구간별로 표시, y축은 누적개수 + +```python +plt.hist(train_len, bins=20) +plt.xlabel('news_len') +plt.ylabel('count')plt.show() +``` + + + +- 라인 차트로 기사 별 길이 시각화 + +```python +plt.figure(figsize=(15, 5)) +plt.plot(train_len) +plt.xlabel('news_index') +plt.ylabel('news_len') +plt.show() +``` + + + +## 시퀀스 길이 맞추는 작업 수행 + +- 기술통계값을 확인해본 결과 시퀀스의 길이를 120으로 맞추는게 적절하다고 생각함 +- 긴 기사는 잘라내고, 짧은 기사는 패딩 작업을 해주기 +- sequence.pad_sequences() : maxlen에 지정된 수만큼 길이를 앞에서부터 자르거나 패딩하여 맞춘다. 옵션을 주면 뒤에서 부터 가능 + +```python +from tensorflow.keras.preprocessing import sequence + +X_train_seq = sequence.pad_sequences(X_train, maxlen=120) +X_test_seq = sequence.pad_sequences(X_test, maxlen=120) +``` + +- 각 단어 데이터는 라벨 인코딩 상태이므로 feature는 1이된다. +- RNN 학습을 위해 input_shape을 맞춰주기 : (samples, timesteps, feature) + +```python +X_train_seq = X_train_seq.reshape(8982, 120, 1) +X_test_seq = X_test_seq.reshape(2246, 120, 1) + +X_train_seq.shape, X_test_seq.shape +``` + +``` +((8982, 120, 1), (2246, 120, 1)) +``` + +## SimpleRNN 모델링 + +- 현재 한개의 feature는 단어의 빈도수를 의미하므로, 빈도수만을 이용해서 예측하는 RNN 모델을 설계하게 되는것이다 +- 다수 입력(input 120) 단일 출력(output 1) 형태 + +```python +from tensorflow.keras import Sequential +from tensorflow.keras.layers import InputLayer, Dense, SimpleRNN +from tensorflow.keras.optimizers import Adam + +model = Sequential() +model.add(InputLayer(input_shape=(120, 1))) +model.add(SimpleRNN(128))model.add(Dense(46, activation='softmax')) + +model.compile(loss='sparse_categorical_crossentropy', + optimizer=Adam(learning_rate=0.001), + metrics=['acc']) +``` + +```python +history = model.fit(X_train_seq, y_train, validation_split=0.2, batch_size=128, epochs=20) +``` + +```python +plt.figure(figsize=(15, 5)) +plt.plot(history.history['acc'], label='train_acc') +plt.plot(history.history['val_acc'], label='val_acc') +plt.legend() +plt.show() +``` + + + +데이터가 빈도수를 나타내는 feature만 존재하긴 하지만, SimpleRNN으로는 높은 성능을 얻기 힘들다는 것을 확인 + +## LSTM 모델 학습 + +- 다수 입력(input 120) 단일 출력(output 1) +- LSTM을 여러 층으로 쌓기 위해서는 이전 LSTM층이 **다수입력 다수출력 상태**가 되어야한다. +- 그래야 **다음 LSTM층의 input 형태가 이전 LSTM층과 같은 형태**가 되기 때문이다 +- LSTM, RNN층에서 순환할때마다 출력값을 만들어주는 (다수입력 다수출력 형태) **return_sequences=True** 옵션을 사용해야 한다. + +```python +from tensorflow.keras.layers import LSTM, GRU + +model2 = Sequential() +model2.add(InputLayer(input_shape=(120, 1))) +model2.add(LSTM(128, return_sequences=True)) +model2.add(LSTM(128))model2.add(Dense(46, activation='softmax')) + +model2.compile(loss='sparse_categorical_crossentropy', + optimizer=Adam(learning_rate=0.001), + metrics=['acc']) +``` + +```python +history2 = model2.fit(X_train_seq, y_train, validation_split=0.2, batch_size=128, epochs=20) +``` + +```python +plt.figure(figsize=(15, 5)) +plt.plot(history2.history['acc'], label='train_acc') +plt.plot(history2.history['val_acc'], label='val_acc') +plt.legend() +plt.show() +``` + + + +## 워드 임베딩(Word Embedding) + +- 언어 모델의 성능을 높이는 방법은 모델의 구조를 변경하는 방법과 단어의 표현방법을 고도화 하는방법이 있다, +- 단어의 표현을 **밀집되게 실수형태로 표현하게 하는 방법**론이 워드 임베딩이다. +- 학습을 통해서 **각 단어들의 수치값을 정밀하게** 만드는 방법이다. + +### 데이터 불러올 때 등장 빈도가 낮은 단어 제거하기 + +- num_words=1500 : reuters의 수치데이터 의미는 단어 빈도수 랭크이다. 해당 옵션을 주면 각 기사내용에 1500위까지의 단어만 가져오게 된다. +- 1500위 외의 단어들은 oov 즉, 2값으로 표현된다. + +```python +from tensorflow.keras.layers import Embedding + +(X_train, y_train), (X_test, y_test) = reuters.load_data(num_words=1500) + +# 시퀀스 길이 맞추기 +X_train_seq = sequence.pad_sequences(X_train, maxlen=120) +X_test_seq = sequence.pad_sequences(X_test, maxlen=120) +``` + +### Embedding 추가 GRU 모델 설계 + +- Embedding(사용하는 단어사전의 수, 한 단어를 표현할 특징의 수)을 가장 앞 층에 추가하기 +- 사용하는 단어 사전의 수는 학습에 사용되는 단어의 갯수이다 +- 한 단어를 표현할 특징의 수는 적절한 수로 설정해서 모델이 알아서 특징을 검출하게 해준다 + +```python +embedding_model = Sequential() + +embedding_model.add(Embedding(1500, 50)) +embedding_model.add(GRU(128, return_sequences=True)) +embedding_model.add(GRU(128)) +embedding_model.add(Dense(46, activation='softmax')) +embedding_model.compile(loss='sparse_categorical_crossentropy', + optimizer=Adam(learning_rate=0.001), + metrics=['acc']) +``` + +```python +history3 = embedding_model.fit(X_train_seq, y_train, epochs=20, validation_split=0.2, batch_size=128) +``` + +```python +plt.figure(figsize=(15, 5)) +plt.plot(history3.history['acc'], label='train_acc') +plt.plot(history3.history['val_acc'], label='val_acc') +plt.legend() +plt.show() +``` + + \ No newline at end of file diff --git "a/_posts/2022-11-09-git01_Git\354\234\274\353\241\234 \355\230\221\354\227\205 \352\270\260\353\212\245 \354\240\225\353\246\254.md" "b/_posts/2022-11-09-git01_Git\354\234\274\353\241\234 \355\230\221\354\227\205 \352\270\260\353\212\245 \354\240\225\353\246\254.md" new file mode 100644 index 000000000000..2c8707df8249 --- /dev/null +++ "b/_posts/2022-11-09-git01_Git\354\234\274\353\241\234 \355\230\221\354\227\205 \352\270\260\353\212\245 \354\240\225\353\246\254.md" @@ -0,0 +1,289 @@ +--- +layout: single +title: "git을 활용한 협업 기능 정리" +categories: git +tag: [git, branch, merge, 에러, fetch, pull] +toc: true +toc_sticky: true + +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 8px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + + .output_prompt { + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; + -webkit-overflow-scrolling: touch; + padding: 0.8rem; + margin-top: 0; + margin-bottom: 15px; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: $code-text-color; + border: solid 1px $border-color; + border-radius: 0.3rem; + word-break: normal; + white-space: pre; + } + + .dataframe tbody tr th:only-of-type { + vertical-align: middle; + } + + .dataframe tbody tr th { + vertical-align: top; + } + + .dataframe thead th { + text-align: center !important; + padding: 8px; + } + + .page__content p { + margin: 0 0 0px !important; + } + + .page__content p > strong { + font-size: 0.8rem !important; + } + + </style> +</head> + +## Git의 branch 관리 + +### 1. **branch 만들기, branch 확인하기** + +브랜치 만들기 : `git branch [원하는 브랜치명]` + +브랜치 리스트 확인 : `git branch` 또는 `git branch —list` + +```bash +$ git branch yoonkie + +$ git branch +* master + yoonkie +``` + +### 2. 만들어진 branch로 이동 + +**git switch [브랜치명]** 또는 **git checkout [브랜치명]** + +```bash +$ git checkout yoonkie +Switched to branch 'yoonkie' +``` + +### switch vs checkout ?? + +기존에 git을 사용하면서 c**heckout**으로 브랜치를 이동했는데 최근 **switch** 명령어를 많이 사용(?)한다고 한다. + +알고보면, switch는 checkout의 분리된 기능이다. + +### switch : 브랜치를 변경한다 + +**switch**를 통해 브랜치를 **변경**할 수도 있고, 기존에 없는 **브랜치를 생성하면서 변경**도 가능 + +( master → yoonkie) + +```bash +$ git switch yoonkie +Switched to branch 'yoonkie' +``` + +### restore : 작업중인 파일을 되돌린다 (복원) + +작업중인 파일 중 기존 **마지막 커밋의 상태로 되돌리고자** 할 때 **restore**를 사용한다 + +```bash +$ git restore README.md +``` + +--- + +## 3. 브랜치 병합하기 Merge + +**git merge** 명령어를 통해 다른 브랜치와 병합할 수 있다. + +1. **matser** 브랜치에서 **git merge yoonkie** 수행 + + : yoonkie 브랜치를 master 브랜치에 병합한다는 뜻 + +2. **yoonkie** 브랜치에서 **git merge master** 수행 + + : master 브랜치를 yoonkie 브랜치에 병합한다는 뜻 + + +### Merge 충돌 관리 + +여려명이 같은 기능, 파일을 수정하면 merge 하면서 **충돌**이 쉽게 발생한다. + +두 명이서 같은 파일을 작업한다면 기존의 내용이 서로 다를 수 있으며, 이를 한쪽에서 merge 하면 git은 **어떤 코드를 반영**해야할지 모른다면서 충돌이 나게 되는것이다. + +> 예시 +> +- master 브랜치에서 수정한 README.md 파일을 commit한 상태 + + + +- yoonkie 브랜치에서 수정한 README.md 파일을 commit한 상태 + + + +- master 브랜치에서 yoonkie 브랜치 merge 수행 + + + +충돌이 발생했다고 표시되며, 브랜치 표시가 **(master|MERGING)**으로 변경된 것을 확인할 수 있다. + +(master|MERGING) : 충돌을 해결하기 위한 임의의 브랜치라고 보면 된다. + + + +> *<<<<<<HEAD* : **현재 브랜치의 이력** +> +> +> *[HEAD]의 코드* +> +> *===========* : **현재 브랜치와 병합 브랜치의 구분선** +> +> *[병합 할 브랜치]의 코드* +> +> *>>>>>>[브랜치 명]* +> + +실제로 README.md파일을 확인하면 위와같이 어떤 변경이 있었는지 확인할 수 있다. + +해당 부분을 **프로젝트 관리자**가 수정 후 다시 commit 하면 된다. + +- **git diff** : 병합 전 어떻게 바뀌었는지 확인할 수 있는 명령어 + +```bash +$ git diff +diff --cc README.md +index aa7a1fc,10703ad..0000000 +--- a/README.md ++++ b/README.md +@@@ -1,3 -1,3 +1,7 @@@ + # literate-engine + +- 마스터가 쓴 텍스트 +++<<<<<<< HEAD +++마스터가 쓴 텍스트 +++======= ++ <p>윤기가 만진 p태그</p> +++>>>>>>> yoonkie +``` + +두 주석 모두 변경사항으로 반영하기로 하고 commit 하기 + + + +그러면 (master|MERGING)에서 다시 (master)브랜치로 돌아오게 된다. + +### Merge 취소하기 + +merge 중에 발생한 충돌을 해결하는 또 다른 방법은 merge 전으로 다시 되돌리는 것이다. + +`git merge —abort` + +### 공백 무시하고 Merge 하기 + +공백때문에 충돌이 나는 경우도 있는데, 해당 충돌이 단순한 공백으로 인해 충돌났을 경우엔 **Merge를 취소**한 다음에 해당 옵션을 주어 다시 Merge하면 된다. + +`git merge -Xignore-space-change [병합할 브랜치명]` + +또는 + +`git merge -Xignore-all-space [병합할 브랜치명]` + +```bash +$ git merge -Xignore-space-change yoonkie +Auto-merging README.md +Merge made by the 'recursive' strategy. + hello.rb | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) +``` + +## Git fetch와 pull의 차이 + +`fetch`는 로컬 Git에게 원격 저장소에서 최신 메타데이터 정보를 확인하라는 명령을 전달하는 것이다. + +즉, `fetch`는 원격 저장소에 **변경사항이 있는지만 확인**하고, 변경된 데이터를 로컬에 실제로 가져오지는 않는다. + +`pull`은 원격 저장소에서 변경된 메타데이터 정보를 확인하고 **최신 데이터를 복사하여 로컬**에 가져온다. + +`fetch` 후 `merge`를 수행하면 `pull` 명령을 실행했을 때와 같은 이력이 만들어진다. + +### Fetch로 변경 사항 먼저 확인하는 법 + +1. fetch 한 후에 FETCH_HEAD의 변경 사항을 확인하면 된다. +2. git diff로 변경사항 확인 +- git fetch + +```bash +$ git fetch origin master +From https://github.com/minyoongi96/SSM-Project + * branch master -> FETCH_HEAD +``` + +- git diff + + + +### Pull로 원격 저장소의 최신 내용을 가져오기 + +```bash +$ git pull origin master +From https://github.com/minyoongi96/SSM-Project + * branch master -> FETCH_HEAD +Updating 85b7a2f..ceda8cd +Fast-forward + README.md | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) +``` + +## LF will be replaced by CRLF in - Git 경고 메시지 뜰 경우 + +txt, md 파일 등에서 개행문자(줄바꿈 : \n 등)가 운영체제마다 인식하는 방법이 달라서 git이 이 부분을 어떤 운영체제에 맞게 인식처리 할 지 몰라서 나는 오류이다. + +- **LF (Line-Feed)** + - Mac, 리눅스 (Unix 계열) 줄바꿈 문자열 : \n +- **CRLF (Carriage-Return + Line-Feed)** + - 윈도우, DOS 줄바꿈 문자열 : \r\n + - CR(\r) + LF(\n) 두 동작을 합친것임 + +### 해결방법 : git config 설정을 변경해주기 + +- 윈도우, DOS인 경우 : git config --global core.autocrlf true 입력 +- Linux, Mac인 경우 : git config --global core.autocrlf input 입력 \ No newline at end of file diff --git "a/_posts/2022-11-09-springboot01_Spring Boot \355\224\204\353\241\234\354\240\235\355\212\270 \354\213\234\354\236\221.md" "b/_posts/2022-11-09-springboot01_Spring Boot \355\224\204\353\241\234\354\240\235\355\212\270 \354\213\234\354\236\221.md" new file mode 100644 index 000000000000..a42d2f4f0c85 --- /dev/null +++ "b/_posts/2022-11-09-springboot01_Spring Boot \355\224\204\353\241\234\354\240\235\355\212\270 \354\213\234\354\236\221.md" @@ -0,0 +1,282 @@ +--- +layout: single +title: "Spring Boot 프로젝트 시작" +categories: SpringBoot +tag: [Java, Spring, Spring Boot, STS, Eclipse] +toc: true +toc_sticky: true + +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 8px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + + .output_prompt { + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; + -webkit-overflow-scrolling: touch; + padding: 0.8rem; + margin-top: 0; + margin-bottom: 15px; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: $code-text-color; + border: solid 1px $border-color; + border-radius: 0.3rem; + word-break: normal; + white-space: pre; + } + + .dataframe tbody tr th:only-of-type { + vertical-align: middle; + } + + .dataframe tbody tr th { + vertical-align: top; + } + + .dataframe thead th { + text-align: center !important; + padding: 8px; + } + + .page__content p { + margin: 0 0 0px !important; + } + + .page__content p > strong { + font-size: 0.8rem !important; + } + + </style> +</head> + + +## STS로 스프링 부트 시작하기 + +- Java Development Kit(JDK): 8이상 (11이상 권장) +- STS 다운로드 : [https://spring.io/tools](https://spring.io/tools) + +## 프로젝트 생성하기 + +Spring Initializer로 프로젝트 세팅 후 STS에서 프로젝트를 시작하는 방법이다. + +[https://start.spring.io/](https://start.spring.io/) + + + +- 프로젝트 환경 + - Tool : STS + - build : Maven + - Language : Java + - Spring Boot Version : 2.7.4 + - Packageing : Jar + - JAVA Version : 8 + +이니셜라이저를 사용하게 되면 프로젝트를 좀 더 간편하게 생성할 수 있다. + +필요한 기능을 사용하기 위해 우측의 Dependencies에서 선택하여 프로젝트를 생성할 수 있기 때문에 초반 세팅에 귀찮은 작업에 낭비되는 시간을 절약할 수 있다. + +프로젝트 셋팅 완료 후 다운로드 받은 후, 해당 파일을 STS의 workspace로 이동시킨다. + +## Spring Boot 프로젝트 설정 + +## 1. porm.xml 설정 + +### **POM(프로젝트 객체 모델(Project Object Model))** + +- Maven의 기능을 이용하기 위해서 POM이 사용된다. +- POM은 pom.xml파일을 말하며 pom.xml은 Maven을 이용하는 프로젝트의 root에 존재하는 xml 파일이다. + + (하나의 자바 프로젝트에 빌드 툴을 Maven으로 설정하면, 프로젝트 최상위 디렉토리에 "pom.xml"이라는 파일이 생성된다.) + +- 파일은 프로젝트마다 1개이며, pom.xml만 보면 프로젝트의 모든 설정, 의존성 등을 알 수 있다. +- 다른 파일이름으로 지정할 수도 있다. (mvn -f 파일명.xml test). 하지만 pom.xml으로 사용하기를 권장 + +**ex) pom.xml 예시** + +```xml +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-parent</artifactId> + <version>2.7.4</version> + <relativePath/> <!-- lookup parent from repository --> + </parent> + <groupId>com.example</groupId> + <artifactId>demo</artifactId> + <version>0.0.1-SNAPSHOT</version> + <name>demo</name> + <description>Demo project for Spring Boot</description> + <properties> + <java.version>1.8</java.version> + </properties> + <dependencies> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-web</artifactId> + </dependency> + <dependency> + <groupId>org.mybatis.spring.boot</groupId> + <artifactId>mybatis-spring-boot-starter</artifactId> + <version>2.2.2</version> + </dependency> + + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-devtools</artifactId> + <scope>runtime</scope> + <optional>true</optional> + </dependency> + <dependency> + <groupId>mysql</groupId> + <artifactId>mysql-connector-java</artifactId> + <scope>runtime</scope> + </dependency> + <dependency> + <groupId>org.projectlombok</groupId> + <artifactId>lombok</artifactId> + <optional>true</optional> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-test</artifactId> + <scope>test</scope> + </dependency> + <!--ThymeLeaf--> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-thymeleaf</artifactId> + </dependency> + + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-maven-plugin</artifactId> + <configuration> + <excludes> + <exclude> + <groupId>org.projectlombok</groupId> + <artifactId>lombok</artifactId> + </exclude> + </excludes> + </configuration> + </plugin> + </plugins> + </build> + +</project> +``` + +### 엘리먼트 + +- modelVersion : POM model의 버전 +- parent : 프로젝트의 계층 정보 +- groupId : 프로젝트를 생성하는 조직의 고유 아이디를 결정한다. 일반적으로 도메인 이름을 거꾸로 적는다. +- artifactId : 프로젝트 빌드시 파일 대표이름 이다. groupId 내에서 유일해야 한다. Maven을 이용하여 빌드시 다음과 같은 규칙으로 파일이 생성 된다. + + artifactid-version.packaging. 위 예의 경우 빌드할 경우 bo-0.0.1-SNAPSHOT.war 파일이 생성된다. (하단 예시 파일 참고) + +- version : 프로젝트의 현재 버전, 프로젝트 개발 중일 때는 SNAPSHOT을 접미사로 사용. +- packaging : 패키징 유형(jar, war, ear 등) +- name : 프로젝트, 프로젝트 이름 +- description : 프로젝트에 대한 간략한 설명 +- url : 프로젝트에 대한 참고 Reference 사이트 +- properties : 버전관리시 용이 하다. ex) 하당 자바 버전을 선언 하고 dependencies에서 다음과 같이 활용 가능 하다. + + <version>${java.version}</version> + +- dependencies : dependencies태그 안에는 프로젝트와 의존 관계에 있는 라이브러리들을 관리 한다. +- build : 빌드에 사용할 플러그인 목록 + +### 라이브러리 + +Spring부트는 spring-boot-starter로 시작하는 라이브러리를 제공한다. starter-parent에 지정된 라이브러리 버전을 따른다. + +- spring-boot-starter-web + + : Spring MVC를 사용한 RESTful서비스를 개발하는데 사용. + +- spring-boot-starter-test + + : Junit, Hamcrest, Mockito를 포함하는 스프링 어플리케이션을 테스트 가능하도록 한다. + +- spring-boot-devtools + + : devtools는 Spring boot에서 제공하는 개발 편의를 위한 모듈이다. 쉽게 말하면 브라우저로 전송되는 내용들에 대한 코드가 변경되면, **자동으로 어플리케이션을 재시작**하여 브라우저에도 업데이트를 해주는 역할을 한다. + +- mybatis-spring-boot-starter + + : 스프링부트 위에 MyBatis 애플리케이션을 빠르게 빌드 할 수 있다. + + - DataSource 를 자동 감지합니다. + - SqlSessionFactory 를 전달 하는 인스턴스를 자동 생성하고 등록합니다 + - DataSource.SqlSessionFactoryBean 의 인스턴스를 만들고 등록합니다. + - @Mapper주석이 표시된 매퍼를 자동 스캔하고에 연결합니다. + - SqlSessionTemplateSpring 컨텍스트에 등록하여 Bean에 주입 할 수 있도록합니다. +- mysql-connector-java + + : 스프링부트에서 MySQL을 사용하기 위한 라이브러리 + +- lombok + + : Java 라이브러리로 반복되는 getter, setter, toString 등의 메서드 작성 코드를 줄여주는 코드 다이어트 라이브러리 + +- spring-boot-starter-thymeleaf + + : ThymeLeaf 템플릿을 사용하기 위한 라이브러리 + + +## 2. application.properties 설정 + +이 파일은 스프링부트가 애플리케이션을 구동할 때 자동으로 로딩하는 파일이다. + +key - value 형식으로 값을 정의하면 애플리케이션에서 참조하여 사용할 수 있다. + + + +- url을 호출할 때 필요한 context-path +- Server 포트 설정 + + 기본 포트는 8080 + +- JSP View Resolver(JSP 사용할 때) +- MySQL 접속 정보 +- mapper.xml 경로 설정(별도의 경로를 사용할 때 설정) +- ThymeLeaf 설정 + + 기본 경로는 classpath:/templates/ \ No newline at end of file diff --git "a/_posts/2022-11-10-springboot04_Spring JPA \354\204\244\354\240\225 \353\260\217 \354\202\254\354\232\251 \354\226\264\353\205\270\355\205\214\354\235\264\354\205\230.md" "b/_posts/2022-11-10-springboot04_Spring JPA \354\204\244\354\240\225 \353\260\217 \354\202\254\354\232\251 \354\226\264\353\205\270\355\205\214\354\235\264\354\205\230.md" new file mode 100644 index 000000000000..bfb3c289a6b3 --- /dev/null +++ "b/_posts/2022-11-10-springboot04_Spring JPA \354\204\244\354\240\225 \353\260\217 \354\202\254\354\232\251 \354\226\264\353\205\270\355\205\214\354\235\264\354\205\230.md" @@ -0,0 +1,252 @@ +--- +layout: single +title: "Spring JPA 설정 및 사용 어노테이션" +categories: SpringBoot +tag: [Java, Spring, JPA, Spring Boot, STS, Eclipse] +toc: true +toc_sticky: true + +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 8px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + + .output_prompt { + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; + -webkit-overflow-scrolling: touch; + padding: 0.8rem; + margin-top: 0; + margin-bottom: 15px; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: $code-text-color; + border: solid 1px $border-color; + border-radius: 0.3rem; + word-break: normal; + white-space: pre; + } + + .dataframe tbody tr th:only-of-type { + vertical-align: middle; + } + + .dataframe tbody tr th { + vertical-align: top; + } + + .dataframe thead th { + text-align: center !important; + padding: 8px; + } + + .page__content p { + margin: 0 0 0px !important; + } + + .page__content p > strong { + font-size: 0.8rem !important; + } + + </style> +</head> + + +## application.yml 설정 + +```yaml +spring: + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + password: 1234 + url: jdbc:mysql://localhost:3306/mysql + username: root + # JPA 설정 + jpa: + database-platform: org.hibernate.dialect.MySQL5InnoDBDialect # 데이터베이스 종류 + hibernate: + ddl-auto: update # jpa로 entity 처음 생성시 테이블 없으면 create, 만든 다음엔 update로 변경해야함 + properties: + hibernate: + format_sql: true + show-sql: true +``` + +## 엔티티와 매핑 + +### @Entity란 ? + +@Entity가 붙은 클래스는 JPA가 관리하는 객체이다. + +```java +@Entity +// 제약조건이나 테이블 명 등의 옵션 설정 가능 +@Table(name = "BOARD2") +@Data +public class Board { + + @Id // PK라는 뜻 + @GeneratedValue(strategy = GenerationType.IDENTITY) // 자동증가 라는 뜻(strategy의 Identity: mysql) + private Long idx; + + @Column(nullable = false) + private String memId; + + @Column(length = 1000) // 컬럼 상세 설정 + private String title; + + @Column(length = 2000) + private String contents; + + @ColumnDefault("0") + private int count; + + @Column(length = 100) + private String writer; + + @Temporal(TemporalType.TIMESTAMP) + @Column(columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP") + private Date indate; + +} +``` + +- 객체와 테이블 매핑 : @Entity, @Table +- 기본키 매핑 : @Id +- 필드와 컬럼 매핑 : @Column +- 연관관계 매핑 : @ManyToOne, @JoinColumn +- 자동증가 : @GeneratedValue(strategy = GenerationType.IDENTITY) + +### 1. @Entity + +- 테이블과의 매핑 +- JPA가 관리하는 것으로 엔티티라고 불림 +- 속성 + - name : JPA에서 사용할 엔티티 이름을 지정, 보통 기본값인 클래스 이름을 사용 + +### 2. @Table + +- 에니티와 매핑할 테이블을 지정 +- 생략 시 매핑한 엔티티 이름을 테이블 이름으로 사용 +- 속성 + - name : 매핑할 테이블 이름 (default : 엔티티 이름 사용) + - catalog : catalog 기능이 있는 DB에서 catalog를 매핑 + - schema : schema 기능이 있는 DB에서 schema를 매핑 + - uniqueConstraints : DDL 생성 시 **유니크 제약조건을 생성**, 스키마 자동생성 기능을 사용해서 DDL을 만들때만 사용됨 + +### 3. @Column + +- 객체 필드를 테이블 컬럼에 매핑 +- 속성 + - name : 필드와 매핑할 테이블 컬럼 이름 (default : 객체의 필드 이름) + - nullable (DDL) : **null 값의 허용 여부** 설정, false : not null (default : true) + - unique (DDL) : @Table의 uniqueConstraints와 같지만 **한 컬럼에 간단히 유니크 제약조건**을 적용 + - columnDefinition (DDL) : 데이터베이스 **컬럼 정보**를 **직접** 줄수 있음 + - length (DDL) : 문자 길이 제약조건, **String** 타입에만 적용 (default : 255) + - percision, scale (DDL) : **BigDecimal, BigInteger** 타입에만 적용, 아주 큰 숫자나 정밀한 소수를 다룰때 사용 (default : precision = 19, scale = 2) + +### 4. @Enumerated + +- 자바의 enum 타입을 매핑할 때 사용 +- EnumType.ORDINAL : enum 순서를 데이터베이스에 저장 +- EnumType.STRING : enum 이름을 데이터베이스에 저장 (default : EnumType.ORDINAL) + +### 5. @Temporal + +- 날짜 타입(java.util.Date, java.util.Calendar)을 매핑할 때 사용 +- 자바의 Date 타입에는 년월일 시분초가 있지만, 데이터베이스에는 date(날짜), time(시간), timestamp(날짜와 시간)라는 세 가지 타입이 별도로 존재 +- @Temporal을 생략하면 자바의 Date와 가장 유사한 **timestamp**로 정의 +- 하지만 timestamp대신에 **datetime**을 예약어로 사용하는 데이터베이스도 있는데, 데이터베이스 방언 덕분에 애플리케이션 코드는 변경하지 않아도 됨 +- datetime : MySQL +- timestamp : H2, 오라클, PostgreSQL + 1. TemporalType.**DATE** + : 날짜, 데이터베이스 date 타입과 매핑 (예 : 2013-10-11) + 2. TemporalType.**TIME** + : 시간, 데이터베이스 time 타입과 매핑 (예 : 11:11:11) + 3. TemporalType.**TIMESTAMP** + : 날짜와 시간, 데이터베이스 timestamp 타입과 매핑 (예 : 2013-10-11 11:11:!1) + +### Default 값을 넣는 방법 + +**@DynamicInsert :** Default값을 적용하기 위해서는 이 어노테이션을 써야한다. Insert 시 지정된 Default값을 적용시킨다. + +**@ColumnDefault :** ColumnDefault는 Default값 설정을 할 때 사용한다. + +## @Builder 어노테이션 올바른 사용 + +Entity를 선언할 때 주의점 + +1. @Setter : 객체가 무분별하게 변경될 가능성이 있다 +2. @NoArgsConstructor : 기본 생성자의 접근 제어자가 불명확하다 +3. @AllArgsConstructor : 객체 내부의 인스턴스멤버들을 모두 가지고 있는 생성자를 생성한다 + +@Builder 어노테이션을 사용하여 문제점들을 해결할 수 있다. + +- 객체 생성자 + +```java +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Member { + + @Id + private String memId; + + @Column(length = 100) + private String memPwd; + + @Column(length = 100) + private String memName; + + // 의미있는 객체 생성 + @Builder + public Member(String memId, String memPwd, String memName) { + this.memId = memId; + this.memPwd = memPwd; + this.memName = memName; + } +} +``` + +- 테스트 코드 + +```java +@Test + void insertMember() { + Member vo = Member.builder() + .memId("admin") + .memPwd("1234") + .memName("관리자").build(); + + mRepository.save(vo); + } +``` \ No newline at end of file diff --git "a/_posts/2022-11-14-springboot05_AssertJ \354\202\254\354\232\251\355\225\264\353\263\264\352\270\260.md" "b/_posts/2022-11-14-springboot05_AssertJ \354\202\254\354\232\251\355\225\264\353\263\264\352\270\260.md" new file mode 100644 index 000000000000..2426423f5207 --- /dev/null +++ "b/_posts/2022-11-14-springboot05_AssertJ \354\202\254\354\232\251\355\225\264\353\263\264\352\270\260.md" @@ -0,0 +1,145 @@ +--- +layout: single +title: "AssertJ 사용해보기" +categories: SpringBoot +tag: [Java, Spring, JPA, Spring Boot, STS, Eclipse, AssertJ, JUnit] +toc: true +toc_sticky: true +post-header: false + +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 8px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + + .output_prompt { + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; + -webkit-overflow-scrolling: touch; + padding: 0.8rem; + margin-top: 0; + margin-bottom: 15px; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: $code-text-color; + border: solid 1px $border-color; + border-radius: 0.3rem; + word-break: normal; + white-space: pre; + } + + .dataframe tbody tr th:only-of-type { + vertical-align: middle; + } + + .dataframe tbody tr th { + vertical-align: top; + } + + .dataframe thead th { + text-align: center !important; + padding: 8px; + } + + .page__content p { + margin: 0 0 0px !important; + } + + .page__content p > strong { + font-size: 0.8rem !important; + } + + </style> +</head> + +## AssertJ의 특징 + +JUit이 기본적으로 제공해주는 Assert는 너무 불편해서 AssertJ를 사용해보았다. + +## 장점 + +- 메소드 체이닝을 지원해주기 때문에 좀 더 깔끔하고 읽기 쉬운 테스트 코드를 작성할 수 있다. +- 테스트를 하면서 필요하다고 상상할 수 있는 거의 모든 메소드를 제공한다. + +## 라이브러리 의존성 설정 + +Java8 이상은 3.x 버전을 사용해야 한다. + +- Gradle + +```yaml +testCompile 'org.assertj:assertj-core:3.6.2' +``` + +- Maven + +```xml +<dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-core</artifactId> + <!-- use 2.6.0 for Java 7 projects --> + <version>3.6.2</version> + <scope>test</scope> +</dependency> +``` + +## 사용방법 + +모든 테스트 코드는 assertThat() 메소드에서 출발한다. 다음과 같은 포맷으로 AssertJ에서 제공하는 다양한 메소드를 연쇄 호출하면서 작성할 수 있다. + +```java +assertThat(테스트 타켓).메소드1().메소드2().메소드3(); +``` + +### 문자열 테스트 + +```java +assertThat("Hello, world! Nice to meet you.") // 주어진 "Hello, world! Nice to meet you."라는 문자열은 + .isNotEmpty() // 비어있지 않고 + .contains("Nice") // "Nice"를 포함하고 + .contains("world") // "world"도 포함하고 + .doesNotContain("ZZZ") // "ZZZ"는 포함하지 않으며 + .startsWith("Hell") // "Hell"로 시작하고 + .endsWith("u.") // "u."로 끝나며 + .isEqualTo("Hello, world! Nice to meet you."); // "Hello, world! Nice to meet you."과 일치합니다. +``` + +### 숫자 테스트 + +```java +assertThat(3.14d) // 주어진 3.14라는 숫자는 + .isPositive() // 양수이고 + .isGreaterThan(3) // 3보다 크며 + .isLessThan(4) // 4보다 작습니다 + .isEqualTo(3, offset(1d)) // 오프셋 1 기준으로 3과 같고 + .isEqualTo(3.1, offset(0.1d)) // 오프셋 0.1 기준으로 3.1과 같으며 + .isEqualTo(3.14); // 오프셋 없이는 3.14와 같습니다 +``` \ No newline at end of file diff --git "a/_posts/2022-11-15-springboot02_Spring Boot \354\227\220\354\204\234 jsessionid \353\254\270\354\240\234 \355\225\264\352\262\260.md" "b/_posts/2022-11-15-springboot02_Spring Boot \354\227\220\354\204\234 jsessionid \353\254\270\354\240\234 \355\225\264\352\262\260.md" new file mode 100644 index 000000000000..d4a2e6d627a1 --- /dev/null +++ "b/_posts/2022-11-15-springboot02_Spring Boot \354\227\220\354\204\234 jsessionid \353\254\270\354\240\234 \355\225\264\352\262\260.md" @@ -0,0 +1,121 @@ +--- +layout: single +title: "Spring Boot 에서 jsessionid 문제 해결" +categories: SpringBoot +tag: [Java, Spring, Spring Boot, STS, Eclipse] +toc: true +toc_sticky: true + +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 8px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + + .output_prompt { + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; + -webkit-overflow-scrolling: touch; + padding: 0.8rem; + margin-top: 0; + margin-bottom: 15px; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: $code-text-color; + border: solid 1px $border-color; + border-radius: 0.3rem; + word-break: normal; + white-space: pre; + } + + .dataframe tbody tr th:only-of-type { + vertical-align: middle; + } + + .dataframe tbody tr th { + vertical-align: top; + } + + .dataframe thead th { + text-align: center !important; + padding: 8px; + } + + .page__content p { + margin: 0 0 0px !important; + } + + .page__content p > strong { + font-size: 0.8rem !important; + } + + </style> +</head> + + +## jsessionid란? + +- jsessionid는 새 세션이 만들어지면 클라이언트가 쿠키를 지원하는지 여부를 서버가 알 수 없으므로, 쿠키와 URL에 모두 jsessionid를 만들어 주는 것을 의미한다. +- url에 붙거나 헤더에 붙여서 표시된다. +- jsessionid를 탈취당하면 **사용자 ID, Password를 몰라도 접근이 가능하게 된다.** + +# 해결방법 + +### 1. application.properties에 아래와 같은 옵션을 추가한다. + + + +### 2. 클래스 계승 시작 SpringBootServletInitializer 재 작성 onStartup 방법 + +```java +@Override +public void onStartup(ServletContext servletContext) throws ServletException { + super.onStartup(servletContext); + servletContext.setSessionTrackingModes(Collections.singleton(SessionTrackingMode.COOKIE)); + SessionCookieConfig sessionCookieConfig=servletContext.getSessionCookieConfig(); + sessionCookieConfig.setHttpOnly(true); +} +``` + +### 3. @ Configuration 설정 클래스 에 bean 등록 + +ServletContextInitializer 클래스를 Bean객체로 등록하여 스프링 부트가 jseesion과 관련된 설정을 읽도록 해 주면 된다. + +```java +@Bean +public ServletContextInitializer servletContextInitializer1() { + return new ServletContextInitializer() { + @Override + public void onStartup(ServletContext servletContext) throws ServletException { + servletContext.setSessionTrackingModes(Collections.singleton(SessionTrackingMode.COOKIE) ); + } + }; +} +``` \ No newline at end of file diff --git "a/_posts/2022-11-16-springboot06_JPA \354\227\260\352\264\200\352\264\200\352\263\204 \353\247\244\355\225\221.md" "b/_posts/2022-11-16-springboot06_JPA \354\227\260\352\264\200\352\264\200\352\263\204 \353\247\244\355\225\221.md" new file mode 100644 index 000000000000..743f93ae9f7a --- /dev/null +++ "b/_posts/2022-11-16-springboot06_JPA \354\227\260\352\264\200\352\264\200\352\263\204 \353\247\244\355\225\221.md" @@ -0,0 +1,169 @@ +--- +layout: single +title: "JPA 연관관계 매핑" +categories: SpringBoot +tag: [Java, Spring, JPA, Spring Boot, join] +toc: true +toc_sticky: true +post-header: false + +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 8px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + + .output_prompt { + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; + -webkit-overflow-scrolling: touch; + padding: 0.8rem; + margin-top: 0; + margin-bottom: 15px; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: $code-text-color; + border: solid 1px $border-color; + border-radius: 0.3rem; + word-break: normal; + white-space: pre; + } + + .dataframe tbody tr th:only-of-type { + vertical-align: middle; + } + + .dataframe tbody tr th { + vertical-align: top; + } + + .dataframe thead th { + text-align: center !important; + padding: 8px; + } + + .page__content p { + margin: 0 0 0px !important; + } + + .page__content p > strong { + font-size: 0.8rem !important; + } + + </style> +</head> + + +## 연관관계 매핑 + +연관관계 매핑이란 객체의 참조와 테이블의 **외래키를 매핑**하는 것을 의미한다. + +**JPA**에서는 **Mybatis**를 사용했을 때와 다르게 연관 관계에 있는 상대 테이블의 PK를 멤버변수로 갖지 않고, 엔티티 **객체 자체를 통째로 참조**한다. + +### 용어 + +- **방향** +1. 단방향 관계 : 두 엔티티가 관계를 맺을 때, **한 쪽의 엔티티만 참조**하고 있는 것을 의미한다. +2. 양방향 관계 : 두 엔티티가 관계를 맺을 때, **양 쪽이 서로 참조**하고 있는 것을 의미한다. + +데이터 모델링에서는 관계를 맺어주기만 하면 자동으로 양방향 관계가 되어 서로 참조하지만, 객체지향 모델링에서는 구현하고자 하는 서비스에 따라 단방향 관계인지, 양방향 관계인지 적절한 선택을 할 필요가 있다. + +하지만, 어느 정도의 비즈니스에서는 단방향 관계만으로도 해결이 가능하기 때문에 양방향 관계를 꼭 해야하는 것은 아니다. + +그리고 양방향 관계란 서로 다른 단방향 관계 2개를 묶어서 양방향인 것처럼 보이게 할 뿐, 양방향 연관 관계는 사실 존재하지 않는다고 할 수 있다. + +- **다중성** +1. ManyToOne : 다대일 (N : 1) +2. OneToMany : 일대다 (1 : N) +3. OneToOne : 일대일 (1 : 1) +4. ManyToMany : 다대다 (N : M) + +- **연관관계의 주인 (Owner)** + +연관 관계에서 주인을 찾는 방법은 관계를 갖는 두 테이블에 대해서 **외래키를 갖는 테이블**이 연관 관계의 주인이라고 할 수 있다. + +연관 관계의 주인만이 **외래키를 등록, 수정, 삭제**할 수 있고, 반면 주인이 아닌 엔티티는 읽기만 할 수 있기 때문이다. + +### 예시 - @ManyToOne (단방향) + +```java +@Entity +@Table +@Data +public class Categories { + @Id + @Column(name = "cate_seq") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long cateSeq; + + @Column(name = "cate_name", length = 400) + private String cateName; +} + +@Entity +@Table +@Data +public class Items { + @Id + @Column(name = "item_seq") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long itemSeq; + + @Column(name = "item_name", length = 45) + private String itemName; + + @Column(name = "item_price") + private int itemPrice; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "cate_seq") + private Categories categoriesVO; +} +``` + +단방향은 한 쪽의 엔티티가 상대 엔티티를 참조하고 있는 상태이다. + +그래서 Items 엔티티에만 @ManyToOne 어노테이션을 추가했다. + +- **@ManyToOne** + + @ManyToOne 어노테이션은 이름 그대로 다대일 관계 매핑 정보이다. + + Items 입장에서는 Categories 와 다대일 관계이므로 ManyToOne 어노테이션을 사용했다. + + 연관 관계를 매핑할 때는 이렇게 다중성을 나타내는 어노테이션을 필수로 사용해야 하며, **엔티티 자신을 기준**으로 다중성을 생각해야 한다. + +- @JoinColumn(name=”cate_seq”) + + @JoinColumn 어노테이션은 외래 키를 매핑할 때 사용한다. + + name 속성에는 매핑할 외래 키 이름을 지정한다. + + Items 엔티티의 경우 Categories 엔티티의 PK를 “cate_seq”라는 이름의 외래키를 가진다는 뜻으로 작성이 된다. \ No newline at end of file diff --git a/_posts/2022-11-18-springboot03_Spring Data JPA.md b/_posts/2022-11-18-springboot03_Spring Data JPA.md new file mode 100644 index 000000000000..fbac83b23e5a --- /dev/null +++ b/_posts/2022-11-18-springboot03_Spring Data JPA.md @@ -0,0 +1,129 @@ +--- +layout: single +title: "Spring Data JPA란?" +categories: SpringBoot +tag: [Java, Spring, JPA, Spring Boot, STS, Eclipse] +toc: true +toc_sticky: true + +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 8px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + + .output_prompt { + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; + -webkit-overflow-scrolling: touch; + padding: 0.8rem; + margin-top: 0; + margin-bottom: 15px; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: $code-text-color; + border: solid 1px $border-color; + border-radius: 0.3rem; + word-break: normal; + white-space: pre; + } + + .dataframe tbody tr th:only-of-type { + vertical-align: middle; + } + + .dataframe tbody tr th { + vertical-align: top; + } + + .dataframe thead th { + text-align: center !important; + padding: 8px; + } + + .page__content p { + margin: 0 0 0px !important; + } + + .page__content p > strong { + font-size: 0.8rem !important; + } + + </style> +</head> + + + +## ORM이란? + +어플리케이션의 객체와 관계형 데이터베이스의 데이터를 자동으로 매핑해주는 것을 의미 + +- Java의 데이터 클래스와 관계형 데이터베이스의 테이블을 매핑 + +객체지향 프로그래밍과 관계형 데이터베이스의 차이로 발생하는 제약사항을 해결해주는 역할을 수행 + +대표적으로 JPA, Hibernate 등이 있음 (Persistent API) + +## ORM의 장점 + +1. SQL 쿼리가 아닌 직관적인 코드로 데이터를 조작할 수 있음 + - 개발자가 보다 비즈니스 로직에 집중할 수 있음 +2. 재사용 및 유지보수가 편리 + - ORM은 독릭접으로 작성되어 있어 재사용이 가능 + - 매핑정보를 명확하게 설계하기 때문에 따로 데이터베이스를 볼 필요가 없음 +3. DBMS에 대한 종속성이 줄어듬 + - DBMS를 교체하는 작업을 비교적 적은 리스크로 수행 가능 + +## ORM의 단점 + +1. 복잡성이 커질 경우 ORM만으로 구현하기 어려움 + - 직접 쿼리를 구현하지 않아 복잡한 설계가 어려움 +2. 잘못 구현할 경우 속도 저하 발생 +3. 대형 쿼리는 별도의 튜닝이 필요할 수 있음 + +## JPA (Java Persistance API) + +### **Hibernate** + +ORM Framework중 하나 + +JPA의 실제 구현체 중 하나이며, 현재 JPA 구현체 중 가장 많이 사용됨 + + + +### Spring Data JPA + +Spring Framework에서 JPA를 편리하게 사용할 수 있게 지원하는 라이브러리 + +- CRUD 처리용 인터페이스 제공 +- Repository 개발 시 인터페이스만 작성하면 구현 객체를 동적으로 생성해서 주입 +- 데이터 접근 계층 개발시 인터페이스만 작성해도 됨 + +Hibernate에서 자주 사용되는 기능을 조금 더 쉽게 사용할 수 있게 구현 \ No newline at end of file diff --git "a/_posts/2022-11-20-springboot07_JPA ,Query \354\226\264\353\205\270\355\205\214\354\235\264\354\205\230, JPQL, DTO \353\247\244\355\225\221, function.md" "b/_posts/2022-11-20-springboot07_JPA ,Query \354\226\264\353\205\270\355\205\214\354\235\264\354\205\230, JPQL, DTO \353\247\244\355\225\221, function.md" new file mode 100644 index 000000000000..b494fda49786 --- /dev/null +++ "b/_posts/2022-11-20-springboot07_JPA ,Query \354\226\264\353\205\270\355\205\214\354\235\264\354\205\230, JPQL, DTO \353\247\244\355\225\221, function.md" @@ -0,0 +1,357 @@ +--- +layout: single +title: "JPA ,Query 어노테이션, JPQL, DTO 매핑, function 사용" +categories: SpringBoot +tag: [Java, Spring, JPA, Spring Boot, join] +toc: true +toc_sticky: true +post-header: false + +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 8px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + + .output_prompt { + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; + -webkit-overflow-scrolling: touch; + padding: 0.8rem; + margin-top: 0; + margin-bottom: 15px; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: $code-text-color; + border: solid 1px $border-color; + border-radius: 0.3rem; + word-break: normal; + white-space: pre; + } + + .dataframe tbody tr th:only-of-type { + vertical-align: middle; + } + + .dataframe tbody tr th { + vertical-align: top; + } + + .dataframe thead th { + text-align: center !important; + padding: 8px; + } + + .page__content p { + margin: 0 0 0px !important; + } + + .page__content p > strong { + font-size: 0.8rem !important; + } + + </style> +</head> + +### JPA 장점 + +- SQL문이 아닌 Method를 통해 DB를 조작할 수 있어 개발자가 비즈니스 로직 구성에 좀 더 집중할 수 있음 +- 쿼리문 등의 부수적인 코드가 줄어들어 가독성이 높아짐 +- 오직 객체지향적 접근만 고려되므로 생산정 증가 +- 매핑 정보가 Class로 명시되어 있어서 ERD를 보는 의존도를 낮출 수 있고 유지보수 및 리팩토링에 유리 + +### JPA 단점 + +- 프로젝트의 규모가 크고 복잡하여 설계가 잘못된 경우, 속도 저하 및 일관성을 무너뜨리는 문제점이 생길 수 있음 +- 복잡하고 무거운 Query는 속도를 위해 별도의 튜닝이 필요하기 때문에 결국 SQL문을 써야할 수도 있음 + +JPA가 쿼리를 자동으로 생성해주지만 상황에 따라 직접 쿼리를 작성할 필요가 생긴다. + +### JPA에서 직접 쿼리를 작성할 수 있는 방법 + +- JPQL 작성 +- 네이티브 쿼리(일반 SQL) 작성 + +JPQL은 JPA의 일부분으로 정의된 플랫폼 독립적인 **객체지향 쿼리 언어**이다. + +**네이티브 쿼리는 데이터베이스를 바라보고 작성**한다면 **JPQL은 엔티티 클래스를 바라보고 작성**해야 한다. + +JPQL에서는 **대 소문자 구분**을 하고 select, from과 같은 **키워드는 구분하지 않는다.** + +## @Query 어노테이션 + +JpaRepository에서 쿼리(JPQL, native query 둘 다)를 직접 작성해줄 때 사용한다. + +메서드 명은 기존 자동생성 방식과 달리 **자유롭게 작성**할 수 있다. + +그리고 **nativeQuery**라는 속성을 이용하여 JPQL로 작성한 것인지 SQL로 작성한 것인지 구분할 수 있다. + +- nativeQuery = false(default) → JPQL +- nativeQuery = true → SQL + +### Entity : Broadcasting (방송 정보) + +```java +@Entity +@Table +@Data +public class Broadcasting { + + @Id + @Column(name = "bc_seq") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long bcSeq; + + @Column(name = "bc_title", nullable = false, length = 400) + private String bcTitle; +``` + +### Entity : ViewerReaction (시청자 반응) + +```java +@Entity +@Table(name = "viewer_reaction") +@Data +public class ViewerReaction { + @Id + @Column(name ="vr_seq") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long vrSeq; + + @Column(name ="vr_viewers", nullable = false) + private int vrViewers; + + @Column(name ="vr_sales", nullable = false) + private int vrSales; + + @Column(name ="vr_comments", nullable = false) + private int vrComments; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "bc_seq") + private Broadcasting broadcastingVO; +} +``` + +### 1. @Query 사용 방법 (일반 쿼리) + +JPQL은 **엔티티의 이름과 속성명을 사용**하고, 일반 SQL은 **설계된 테이블명과 칼럼명** 사용 + +```java +@Repository +public interface ViewerReactionRepository extends JpaRepository<ViewerReaction, Long> { + + // JPQL 사용 + @Query(value = "select vr.vrSeq from ViewerReaction vr") + List<Long> selectAllVrSeq(); + // 일반 SQL 사용 + @Query(value = "select vr.vr_seq from viewer_reaction vr", nativeQuery = true) + List<Long> selectAllVr_seq(); +} +``` + +### 2. @Query 사용 방법 (파라미터 사용) + +```java +@Repository +public interface ViewerReactionRepository extends JpaRepository<ViewerReaction, Long> { + + // JPQL 일반 파라미터 쿼리, @Param 사용 X, 엔티티 변수명 "vrSeq" 사용 + @Query(value = "select vr from ViewerReaction " + + "vr where vr.vrSeq = ?1") + List<ViewerReaction> selectAllByVrSeq(long seq); + + // 일반 SQL문 파라미터 쿼리 ,@Param 사용 X, 테이블 컬럼명 "vr_seq" 사용 + @Query(value = "select * from viewer_reaction " + + "where vr_seq = ?1", nativeQuery = true) + List<ViewerReaction> selectAllByVr_seq(long seq); + + // JPQL 일반 파라미터 쿼리, @Param 사용 O + @Query(value = "select vr from ViewerReaction vr where vr.vrSeq = :seq") + List<ViewerReaction> selectAllByVrSeq2(@Param(value = "seq") long seq); + + // 일반 SQL문 파라미터 쿼리 ,@Param 사용 O + @Query(value = "select * from viewer_reaction " + + "where vr_seq = :seq", nativeQuery = true) + List<ViewerReaction> selectAllByVr_seq2(@Param(value = "seq") long seq); + + // JPQL 객체 파라미터 쿼리, :#{#객체명}으로 사용 + @Query(value = "select vr.vrTitle from ViewerReaction vr " + + "where vr.vrSeq < :#{#bc.bcSeq}") + List<String> selectVrTitleByBc(@Param(value = "bc") Broadcasting bc) + + // 일반 SQL문 객체 파라미터 쿼리, :#{#객체명}으로 사용 + @Query(value = "select vr_title from viewer_reaction " + +"where vr_seq < :#{#bc.bcSeq}", nativeQuery = true) + List<String> selectVrTitleByBc(@Param(value = "bc") Broadcasting bc) +``` + +### 3. @Query 사용 방법(집계 함수 단독) + +객체 파라미터를 사용할 때 여기서는 ManyToOne 매핑이 되어 ViewerReaction 엔티티가 Broadcasting의 매핑 주인이 된다. + +그러므로, JPQL 사용 할 때에는 외래키 속성 변수가 객체 형태가 되므로 주의해서 참고 + +```java +@Repository +public interface ViewerReactionRepository extends JpaRepository<ViewerReaction, Long> { + + // 집계함수 SUM 사용 + // JPQL 사용, broadcastingVO 형태 + @Query(value = "select SUM(vr.vrSales) from ViewerReaction vr " + + "where vr.broadcastingVO = :#{#bc}") + Integer vrSalesSum(@Param("bc") Broadcasting bc); + + // 일반 SQL 사용, 테이블에선 컬럼이 객체 형태가 아님 + @Query(value = "select SUM(vr.vr_sales) from viewer_reaction vr " + + "where vr.bc_seq = :bcSeq", nativeQuery = true) + Integer vrSalesSum(@Param("bcSeq") long bcSeq); +``` + +## DTO Mapping + +JPQL을 작성하면서 여러 `function`과 `join`을 사용하다보면 결과가 Entity 형태로 나오지 않을 경우도 있다. 이를 위해 **DTO 반환**이 필요하다. + +**@Query** 어노테이션을 사용하여 DTO 반환을 하기 위해서는 **select 구분에서 생성자를 통해 객체를 반환**해야 한다. + +### Entity : User + +```java +@Entity +@Table(name = "user") +@Data +public class User { + @Id + private String id; + private String name; + private String phone; + private String deptId; +} +``` + +### JpaRepository + +```java +public interface UserRepository extends JpaRepository<User, String> { + @Query(value = "select u from User u where u.name = :name") + List<User> findByName(@Param("name") String name); +} +``` + +### DTO + +```java +@Data +public class UserDTO { + private String id; + private String name; + private String deptId; + private String deptName; +} +``` + +### @Query로 DTO 반환하기 + +```java +public interface UserRepository extends JpaRepository<User, String> { + @Query(value = "select" + + "new com.yoonkie.dto.UserDto(u.id, u.name, u.deptId, d.deptName " + + "from User u left outer join Dept d on u.deptId = d.deptId") + List<UserDTO> findUserDept(); +} +``` + +## SQL Function + +JPQL에서는 기본적으로 select 구문의 **max, min, count, sum, avg**를 제공하며 `기본 function` 으로는 **COALESCE, LOWER, UPPER** 등을 지원하며 자세한 Function은 문서를 참고하면 된다. + +```java +public interface UserRepository extends JpaRepository<User, String> { + @Query(value = "select max(u.id) " + + "from User user " + + "where u.deptId is not null") + String findMaxUserId(); +} +``` + +이러한 JPQL에서 기본적으로 지원하는 `ANSI Query Function`만으로는 비지니스 조회를 해결하기에 한계가 존재한다. + +`DataBase Function` 을 사용하는 방식은 JPQL에서 `function()` 을 활용하여 **hibernate에 등록된** 각 DataBase의 `Dialect에 정의된 function`을 사용하는 방법이 있다. + +```java +public interface UserRepository extends JpaRepository<User, String> { + @Query(value = "select function('date_format', :date, '%Y/%m/%d') " + + "from User u" + String findNow(@Param("date") LocalDateTime date); +} +``` + +하지만, **hibernate**에서 기본적으로 등록되는 function에서도 **누락되는 function이 존재**한다. 이러한 경우 `MetadataBuilderContributor`의 **구현체를 구현하는 방식**으로 사용할 수 있다. + +(이전에는 **Dialect를 상속받아 구현하는 방식** 사용) + +```java +public class MyMetadataBuilderContributor implements MetadataBuilderContributor{ + @Override + public void contribute(MetadataBuilder metadataBuilder) { + metadataBuilder.applySqlFunction("JSON_EXTRACT", new StandardSQLFunction("JSON_EXTRACT", StringType.INSTANCE)) + .applySqlFunction("JSON_UNQUOTE", new StandardSQLFunction("JSON_UNQUOTE", StringType.INSTANCE)) + .applySqlFunction("STR_TO_DATE", new StandardSQLFunction("STR_TO_DATE", LocalDateType.INSTANCE)) + .applySqlFunction("MATCH_AGAINST", new SQLFunctionTemplate(DoubleType.INSTANCE, "MATCH (?1) AGAINST (?2 IN BOOLEAN MODE)")) + } +} +``` + +`applySqlFunction`의 첫번째 파라미터는 **JPQL**에서 `function(”함수명”)`에서 **함수명**에 해당하는 등록명이다. + +`StandardSQLFunction`은 **기본적인 함수를 등록하기 위한 Class**로 생성자의 **첫번째 파라미터**는 `실제 DataVase Function명`이며, **두번째 파라미터**는 function의 `리턴 타입`이 되야 한다. + +`StandardSQLFunction`의 경우 파라미터는 함수에 순서에 맞게 **JPQL function(’등록 함수명’, 파라미터1, 파라미터2, …) 같이 정의**하여 사용하면 된다. + +`SQLFunctionTemplete`은 **문법이 존재하는 function을 등록할 때 사용가능**하며, **첫번째 파라미터**가 `function의 리턴타입`이고, **두번째 파라미터**가 `function`이다. + +**?1, ?2**와 같이 명시하여 JPQL function()에서 전달되는 **파라미터**의 순서대로 파싱된다. + +```java +public interface UserRepository extends JpaRepository<User, String> { + @Query(value = "select u from User u " + + "where function('JSON_UNQUOTE', function('JSON_EXTRACT', u.phone, '$.id')) = 'admin' ") + List<User> findAllByPhoneAdmin(); +} +``` + +```java +public interface UserRepository extends JpaRepository<User, String> { + @Query(value = "select user from User u" + + "where function('MATCH_AGAINST', u.name, :name) > 0") + List<User> findAllByName(@Param("name") String name); + +} +``` \ No newline at end of file diff --git "a/_posts/2022-11-23-springboot04_Spring JPA \354\204\244\354\240\225 \353\260\217 \354\202\254\354\232\251 \354\226\264\353\205\270\355\205\214\354\235\264\354\205\230.md" "b/_posts/2022-11-23-springboot04_Spring JPA \354\204\244\354\240\225 \353\260\217 \354\202\254\354\232\251 \354\226\264\353\205\270\355\205\214\354\235\264\354\205\230.md" new file mode 100644 index 000000000000..bfb3c289a6b3 --- /dev/null +++ "b/_posts/2022-11-23-springboot04_Spring JPA \354\204\244\354\240\225 \353\260\217 \354\202\254\354\232\251 \354\226\264\353\205\270\355\205\214\354\235\264\354\205\230.md" @@ -0,0 +1,252 @@ +--- +layout: single +title: "Spring JPA 설정 및 사용 어노테이션" +categories: SpringBoot +tag: [Java, Spring, JPA, Spring Boot, STS, Eclipse] +toc: true +toc_sticky: true + +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 8px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + + .output_prompt { + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; + -webkit-overflow-scrolling: touch; + padding: 0.8rem; + margin-top: 0; + margin-bottom: 15px; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: $code-text-color; + border: solid 1px $border-color; + border-radius: 0.3rem; + word-break: normal; + white-space: pre; + } + + .dataframe tbody tr th:only-of-type { + vertical-align: middle; + } + + .dataframe tbody tr th { + vertical-align: top; + } + + .dataframe thead th { + text-align: center !important; + padding: 8px; + } + + .page__content p { + margin: 0 0 0px !important; + } + + .page__content p > strong { + font-size: 0.8rem !important; + } + + </style> +</head> + + +## application.yml 설정 + +```yaml +spring: + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + password: 1234 + url: jdbc:mysql://localhost:3306/mysql + username: root + # JPA 설정 + jpa: + database-platform: org.hibernate.dialect.MySQL5InnoDBDialect # 데이터베이스 종류 + hibernate: + ddl-auto: update # jpa로 entity 처음 생성시 테이블 없으면 create, 만든 다음엔 update로 변경해야함 + properties: + hibernate: + format_sql: true + show-sql: true +``` + +## 엔티티와 매핑 + +### @Entity란 ? + +@Entity가 붙은 클래스는 JPA가 관리하는 객체이다. + +```java +@Entity +// 제약조건이나 테이블 명 등의 옵션 설정 가능 +@Table(name = "BOARD2") +@Data +public class Board { + + @Id // PK라는 뜻 + @GeneratedValue(strategy = GenerationType.IDENTITY) // 자동증가 라는 뜻(strategy의 Identity: mysql) + private Long idx; + + @Column(nullable = false) + private String memId; + + @Column(length = 1000) // 컬럼 상세 설정 + private String title; + + @Column(length = 2000) + private String contents; + + @ColumnDefault("0") + private int count; + + @Column(length = 100) + private String writer; + + @Temporal(TemporalType.TIMESTAMP) + @Column(columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP") + private Date indate; + +} +``` + +- 객체와 테이블 매핑 : @Entity, @Table +- 기본키 매핑 : @Id +- 필드와 컬럼 매핑 : @Column +- 연관관계 매핑 : @ManyToOne, @JoinColumn +- 자동증가 : @GeneratedValue(strategy = GenerationType.IDENTITY) + +### 1. @Entity + +- 테이블과의 매핑 +- JPA가 관리하는 것으로 엔티티라고 불림 +- 속성 + - name : JPA에서 사용할 엔티티 이름을 지정, 보통 기본값인 클래스 이름을 사용 + +### 2. @Table + +- 에니티와 매핑할 테이블을 지정 +- 생략 시 매핑한 엔티티 이름을 테이블 이름으로 사용 +- 속성 + - name : 매핑할 테이블 이름 (default : 엔티티 이름 사용) + - catalog : catalog 기능이 있는 DB에서 catalog를 매핑 + - schema : schema 기능이 있는 DB에서 schema를 매핑 + - uniqueConstraints : DDL 생성 시 **유니크 제약조건을 생성**, 스키마 자동생성 기능을 사용해서 DDL을 만들때만 사용됨 + +### 3. @Column + +- 객체 필드를 테이블 컬럼에 매핑 +- 속성 + - name : 필드와 매핑할 테이블 컬럼 이름 (default : 객체의 필드 이름) + - nullable (DDL) : **null 값의 허용 여부** 설정, false : not null (default : true) + - unique (DDL) : @Table의 uniqueConstraints와 같지만 **한 컬럼에 간단히 유니크 제약조건**을 적용 + - columnDefinition (DDL) : 데이터베이스 **컬럼 정보**를 **직접** 줄수 있음 + - length (DDL) : 문자 길이 제약조건, **String** 타입에만 적용 (default : 255) + - percision, scale (DDL) : **BigDecimal, BigInteger** 타입에만 적용, 아주 큰 숫자나 정밀한 소수를 다룰때 사용 (default : precision = 19, scale = 2) + +### 4. @Enumerated + +- 자바의 enum 타입을 매핑할 때 사용 +- EnumType.ORDINAL : enum 순서를 데이터베이스에 저장 +- EnumType.STRING : enum 이름을 데이터베이스에 저장 (default : EnumType.ORDINAL) + +### 5. @Temporal + +- 날짜 타입(java.util.Date, java.util.Calendar)을 매핑할 때 사용 +- 자바의 Date 타입에는 년월일 시분초가 있지만, 데이터베이스에는 date(날짜), time(시간), timestamp(날짜와 시간)라는 세 가지 타입이 별도로 존재 +- @Temporal을 생략하면 자바의 Date와 가장 유사한 **timestamp**로 정의 +- 하지만 timestamp대신에 **datetime**을 예약어로 사용하는 데이터베이스도 있는데, 데이터베이스 방언 덕분에 애플리케이션 코드는 변경하지 않아도 됨 +- datetime : MySQL +- timestamp : H2, 오라클, PostgreSQL + 1. TemporalType.**DATE** + : 날짜, 데이터베이스 date 타입과 매핑 (예 : 2013-10-11) + 2. TemporalType.**TIME** + : 시간, 데이터베이스 time 타입과 매핑 (예 : 11:11:11) + 3. TemporalType.**TIMESTAMP** + : 날짜와 시간, 데이터베이스 timestamp 타입과 매핑 (예 : 2013-10-11 11:11:!1) + +### Default 값을 넣는 방법 + +**@DynamicInsert :** Default값을 적용하기 위해서는 이 어노테이션을 써야한다. Insert 시 지정된 Default값을 적용시킨다. + +**@ColumnDefault :** ColumnDefault는 Default값 설정을 할 때 사용한다. + +## @Builder 어노테이션 올바른 사용 + +Entity를 선언할 때 주의점 + +1. @Setter : 객체가 무분별하게 변경될 가능성이 있다 +2. @NoArgsConstructor : 기본 생성자의 접근 제어자가 불명확하다 +3. @AllArgsConstructor : 객체 내부의 인스턴스멤버들을 모두 가지고 있는 생성자를 생성한다 + +@Builder 어노테이션을 사용하여 문제점들을 해결할 수 있다. + +- 객체 생성자 + +```java +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Member { + + @Id + private String memId; + + @Column(length = 100) + private String memPwd; + + @Column(length = 100) + private String memName; + + // 의미있는 객체 생성 + @Builder + public Member(String memId, String memPwd, String memName) { + this.memId = memId; + this.memPwd = memPwd; + this.memName = memName; + } +} +``` + +- 테스트 코드 + +```java +@Test + void insertMember() { + Member vo = Member.builder() + .memId("admin") + .memPwd("1234") + .memName("관리자").build(); + + mRepository.save(vo); + } +``` \ No newline at end of file diff --git "a/_posts/2022-11-23-springboot05_AssertJ \354\202\254\354\232\251\355\225\264\353\263\264\352\270\260.md" "b/_posts/2022-11-23-springboot05_AssertJ \354\202\254\354\232\251\355\225\264\353\263\264\352\270\260.md" new file mode 100644 index 000000000000..2426423f5207 --- /dev/null +++ "b/_posts/2022-11-23-springboot05_AssertJ \354\202\254\354\232\251\355\225\264\353\263\264\352\270\260.md" @@ -0,0 +1,145 @@ +--- +layout: single +title: "AssertJ 사용해보기" +categories: SpringBoot +tag: [Java, Spring, JPA, Spring Boot, STS, Eclipse, AssertJ, JUnit] +toc: true +toc_sticky: true +post-header: false + +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 8px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + + .output_prompt { + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; + -webkit-overflow-scrolling: touch; + padding: 0.8rem; + margin-top: 0; + margin-bottom: 15px; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: $code-text-color; + border: solid 1px $border-color; + border-radius: 0.3rem; + word-break: normal; + white-space: pre; + } + + .dataframe tbody tr th:only-of-type { + vertical-align: middle; + } + + .dataframe tbody tr th { + vertical-align: top; + } + + .dataframe thead th { + text-align: center !important; + padding: 8px; + } + + .page__content p { + margin: 0 0 0px !important; + } + + .page__content p > strong { + font-size: 0.8rem !important; + } + + </style> +</head> + +## AssertJ의 특징 + +JUit이 기본적으로 제공해주는 Assert는 너무 불편해서 AssertJ를 사용해보았다. + +## 장점 + +- 메소드 체이닝을 지원해주기 때문에 좀 더 깔끔하고 읽기 쉬운 테스트 코드를 작성할 수 있다. +- 테스트를 하면서 필요하다고 상상할 수 있는 거의 모든 메소드를 제공한다. + +## 라이브러리 의존성 설정 + +Java8 이상은 3.x 버전을 사용해야 한다. + +- Gradle + +```yaml +testCompile 'org.assertj:assertj-core:3.6.2' +``` + +- Maven + +```xml +<dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-core</artifactId> + <!-- use 2.6.0 for Java 7 projects --> + <version>3.6.2</version> + <scope>test</scope> +</dependency> +``` + +## 사용방법 + +모든 테스트 코드는 assertThat() 메소드에서 출발한다. 다음과 같은 포맷으로 AssertJ에서 제공하는 다양한 메소드를 연쇄 호출하면서 작성할 수 있다. + +```java +assertThat(테스트 타켓).메소드1().메소드2().메소드3(); +``` + +### 문자열 테스트 + +```java +assertThat("Hello, world! Nice to meet you.") // 주어진 "Hello, world! Nice to meet you."라는 문자열은 + .isNotEmpty() // 비어있지 않고 + .contains("Nice") // "Nice"를 포함하고 + .contains("world") // "world"도 포함하고 + .doesNotContain("ZZZ") // "ZZZ"는 포함하지 않으며 + .startsWith("Hell") // "Hell"로 시작하고 + .endsWith("u.") // "u."로 끝나며 + .isEqualTo("Hello, world! Nice to meet you."); // "Hello, world! Nice to meet you."과 일치합니다. +``` + +### 숫자 테스트 + +```java +assertThat(3.14d) // 주어진 3.14라는 숫자는 + .isPositive() // 양수이고 + .isGreaterThan(3) // 3보다 크며 + .isLessThan(4) // 4보다 작습니다 + .isEqualTo(3, offset(1d)) // 오프셋 1 기준으로 3과 같고 + .isEqualTo(3.1, offset(0.1d)) // 오프셋 0.1 기준으로 3.1과 같으며 + .isEqualTo(3.14); // 오프셋 없이는 3.14와 같습니다 +``` \ No newline at end of file diff --git "a/_posts/2022-11-27-springboot08_Spring \355\224\204\353\241\234\354\240\235\355\212\270 \352\263\204\354\270\265 \354\225\204\355\202\244\355\205\215\354\262\230 .md" "b/_posts/2022-11-27-springboot08_Spring \355\224\204\353\241\234\354\240\235\355\212\270 \352\263\204\354\270\265 \354\225\204\355\202\244\355\205\215\354\262\230 .md" new file mode 100644 index 000000000000..e3ef9128a3ca --- /dev/null +++ "b/_posts/2022-11-27-springboot08_Spring \355\224\204\353\241\234\354\240\235\355\212\270 \352\263\204\354\270\265 \354\225\204\355\202\244\355\205\215\354\262\230 .md" @@ -0,0 +1,162 @@ +--- +layout: single +title: "Spring 프로젝트 계층 아키텍처" +categories: SpringBoot +tag: [Java, Spring, JPA, Spring Boot, 아키텍쳐] +toc: true +toc_sticky: true +post-header: false + +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 8px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + + .output_prompt { + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; + -webkit-overflow-scrolling: touch; + padding: 0.8rem; + margin-top: 0; + margin-bottom: 15px; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: $code-text-color; + border: solid 1px $border-color; + border-radius: 0.3rem; + word-break: normal; + white-space: pre; + } + + .dataframe tbody tr th:only-of-type { + vertical-align: middle; + } + + .dataframe tbody tr th { + vertical-align: top; + } + + .dataframe thead th { + text-align: center !important; + padding: 8px; + } + + .page__content p { + margin: 0 0 0px !important; + } + + .page__content p > strong { + font-size: 0.8rem !important; + } + + </style> +</head> + + +스프링의 계층은 **Presentation Layer**, **Business, Layer**, **Data Access Layer** 크게 3개로 나눌 수 있다. + + + +## 각 계층의 역할 및 특징 정리 + +### 프레젠테이션 계층 + +- 브라우저상의 웹 클라이언트의 요청 및 응답을 처리하는 계층이다 +- Service 계층, Data Access 계층에서 발생하는 Exception을 처리 +- @Controller 어노테이션을 사용하여 작성된 Controller 클래스가 이 계층에 속함 + +### 서비스 계층 + +- 애플리케이션 비즈니스 로직 처리와 비즈니스와 관련된 도메인 모델의 적합성 검증 +- 트랜잭션을 관리한다 +- Presentation 계층과 Data Access 계층 사이에서 직접적으로 통신하지 않게 함 +- Service 인터페이스와 @Service 어노테이션을 사용하여 작성된 Service 구현 클래스가 이 계층에 속함 + +### 데이터 엑세스 계층 + +- ORM(Mybatis 혹은 Hibernate)를 주로 사용하는 계층 +- DAO 인터페이스와 @Repository 어노테이션을 사용하여 작성된 DAO 구현 클래스가 이 계층에 속함 +- 데이터베이스에 CRUD하는 계층 + +### 프로젝트에서 직접 작성했던 Repository 인터페이스 + + + +### 도메인 모델 계층 + +- DB의 테이블과 매칭될 클래스 +- Entity 클래스라고도 부른다. + +## 계층에 관한 용어 정리 + +### DTO (Data Transfer Object) + +- 각 계층간 데이터 교환을 위한 객체 (데이터를 주고 받을 형태 혹은 포맷) +- Domain, VO라고도 부름 +- DB에서 데이터를 얻어 Service, Controller 등으로 보낼때 사용함 +- 로직을 갖지 않고 순수하게 getter, setter 메소드를 가진다. + + + +### DAO (Data Access Object) + +- DB에 접근하는 객체 혹은 DB를 사용해 데이터를 조작하는 기능을 하는 객체 +- MyBatis 사용시에 DAO or Mapper, JPA 사용시에 Repository +- Service 계층과 DB를 연결하는 고리 역할을 한다. + +### Entity 클래스 + +- Domain 이라고도 부름 (JPA에서 사용) +- 실제 DB 테이블과 매칭될 클래스 +- Entity 클래스 또는 가장 Core한 클래스라고 부름 +- Domain 로직만을 가지고 있어야하며 Presentation Logic을 가지고 있어서는 안됨 + + + +## Domain 클래스와 DTO 클래스를 분리하는 이유 + + + +- 테이블과 매핑되는 Entity 클래스가 변경되면 여러 클래스에 영향을 끼치게 되지만 View와 통신하는 DTO 클래스는 자주 변경되므로 분리할 필요가 있음 +- DTO는 Domain Model을 복사한 형태로, 다양한 Presentation Logic을 추가한 정도로 사용함 +- View Layer와 DB Layer의 역할을 철저하게 분리하기 위해 + +## RestController와 @Service를 나누는 이유 + +- 모든 기능들을 세분화해서 서비스 계층에 작성하고 나중에는 서비스의 기능들을 **조합**만 해서 새로운 기능으로 만들 수 있음 +- 중복되는 코드가 발생하면 따로 모듈화를 해서 나눠주면 **유지보수**하기 편리하다. +- 비즈니스 로직을 서비스 구현체에서 구현하여 **확장성**과 **재사용성** 그리고 **중복 코드 제거**를 확보할 수 있다. + +### Service 계층에서 비즈니스 로직 처리 + +프로젝트에서 내가 직접 구현했던 Service 구현 클래스 + + \ No newline at end of file diff --git "a/_posts/2022-11-5-DL08-1_RNN \355\231\234\354\232\251 \353\260\217 \354\213\244\354\212\265.md" "b/_posts/2022-11-5-DL08-1_RNN \355\231\234\354\232\251 \353\260\217 \354\213\244\354\212\265.md" new file mode 100644 index 000000000000..4a603e60c40c --- /dev/null +++ "b/_posts/2022-11-5-DL08-1_RNN \355\231\234\354\232\251 \353\260\217 \354\213\244\354\212\265.md" @@ -0,0 +1,387 @@ +--- +layout: single +title: "RNN 실습, 뉴스 기사 분류" +categories: 딥러닝 +tag: [딥러닝, python, 순환 신경망, RNN, 다중분류] +toc: true +toc_sticky: true + +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 8px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + + .output_prompt { + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; + -webkit-overflow-scrolling: touch; + padding: 0.8rem; + margin-top: 0; + margin-bottom: 15px; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: $code-text-color; + border: solid 1px $border-color; + border-radius: 0.3rem; + word-break: normal; + white-space: pre; + } + + .dataframe tbody tr th:only-of-type { + vertical-align: middle; + } + + .dataframe tbody tr th { + vertical-align: top; + } + + .dataframe thead th { + text-align: center !important; + padding: 8px; + } + + .page__content p { + margin: 0 0 0px !important; + } + + .page__content p > strong { + font-size: 0.8rem !important; + } + + </style> +</head> + +# RNN을 활용한 로터스 뉴스 분류 + +- 영국의 뉴스 통신사 로이터의 ’기사 내용’이 들어가면 어떤 ’주제’인지 분류하는 RNN 모델 실습 + +## keras에서 제공하는 데이터셋 불러오기 + +- 실제 뉴스 기사의 내용이 이미 단어단위로 토큰화, 라벨 인코딩 되어 저장이 된 상태이다. +- 단어가 많은 경우 원핫인코딩 상태로 저장을 한다면, 저장공간 측면에서 매우 비효율적임 +- 또한, 텍스트 처리 분야에서는 문제 데이터에 라벨 인코딩을 활용하면 단어의 빈도수까지 고려해줄 수 있음 +- 1: 뉴스의 시작을 알리는 인덱스 번호 +- 2 : oov(Out of Vocabulary)로 사용 +- 3 ~ : 뉴스 전체 내용에서 빈도수 기준으로 랭크를 나타낸다 + +```python +import numpy as np +import pandas as pd +import matplotlib.pyplot as plt +from tensorflow.keras.datasets import reuters + +data = reuters.load_data() +(X_train, y_train), (X_test, y_test) = data + +X_train.shape, y_train.shape, X_test.shape, y_test.shape +``` + +``` +((8982,), (8982,), (2246,), (2246,)) +``` + +0번째 훈련 데이터 에는 87개의 단어가 포함되어 있다. + +```python +len(X_train[0]) +``` + +``` +87 +``` + +### 로이터 뉴스 기사의 단어들이 어떤 숫자로 인코딩 되어 있는지 확인 + +1. reuters.get_word_index()로 확인이 가능 +- key, value 쌍의 딕셔너리 형태 + +```python +news_words = reuters.get_word_index() +news_words +``` + +``` +{'mdbl': 10996, + 'fawc': 16260, + 'degussa': 12089, + 'woods': 8803, + 'hanging': 13796, + ...} +``` + +1. value값 기준으로 news_words 정렬하기 + +```python +sorted(news_words.items(), key=lambda x : x[1]) +``` + +``` +[('the', 1), + ('of', 2), + ('to', 3), + ('in', 4), + ('said', 5), + ...] +``` + +1. 기사가 어떤 단어로 구성되어 있는지 단어들을 이어붙여 확인해보기 +- key값과 value 값의 위치를 교체 + +```python +word_of_news = {} + +for k, v in news_words.items(): + word_of_news[v] = k +``` + +- 단어 이어 붙이기 + +```python +print(' '.join([word_of_news[w] for w in X_train[0]])) +``` + +``` +the wattie nondiscriminatory mln loss for plc said at only ended said commonwealth could 1 traders now april 0 a after said from 1985 and from foreign 000 april 0 prices its account year a but in this mln home an states earlier and rise and revs vs 000 its 16 vs 000 a but 3 psbr oils several and shareholders and dividend vs 000 its all 4 vs 000 1 mln agreed largely april 0 are 2 states will billion total and against 000 pct dlrs +``` + +## 뉴스 주제의 개수를 알아보기 + +- 뉴스 카테고리 갯수 : 46가지 + +```python +np.unique(y_train) +``` + +``` +array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45]) +``` + +## 데이터 가공 + +- timestpes를 설정해야 하는데 각 문제 데이터(X_train)가 포함하는 단어의 갯수는 모두 다르다. +- 그러므로, 문제 데이터의 **단어 개수를 같게(입력 시퀀스 맞춰주기)** 맞춰줘야 학습이 가능하다. +- 즉, **긴 기사는 잘라내고, 짧은 기사는 붙여 넣는 작업**이 필요하다. + +### 시퀀스 길이 맞추기 + +몇번 순환 시킬지(timesteps) 고정 시켜주기 위함 - 각 문제 데이터의 길이들을 불러와서 기술 통계값 확인해보기 + +```python +# 각 문제 데이터의 길이 저장 +train_len = [len(x) for x in X_train] + +# 기술 통계값 확인 +print(f'최댓값 : {max(train_len)}') +print(f'최솟값 : {min(train_len)}') +print(f'평균값 : {np.mean(train_len)}') +print(f'중앙값 : {np.median(train_len)}') +``` + +``` +최댓값 : 2376 +최솟값 : 13 +평균값 : 145.5398574927633 +중앙값 : 95.0 +``` + +- 히스토그램으로 데이터 밀도 구성 확인 +- x축은 뉴스 길이를 각 구간별로 표시, y축은 누적개수 + +```python +plt.hist(train_len, bins=20) +plt.xlabel('news_len') +plt.ylabel('count')plt.show() +``` + + + +- 라인 차트로 기사 별 길이 시각화 + +```python +plt.figure(figsize=(15, 5)) +plt.plot(train_len) +plt.xlabel('news_index') +plt.ylabel('news_len') +plt.show() +``` + + + +## 시퀀스 길이 맞추는 작업 수행 + +- 기술통계값을 확인해본 결과 시퀀스의 길이를 120으로 맞추는게 적절하다고 생각함 +- 긴 기사는 잘라내고, 짧은 기사는 패딩 작업을 해주기 +- sequence.pad_sequences() : maxlen에 지정된 수만큼 길이를 앞에서부터 자르거나 패딩하여 맞춘다. 옵션을 주면 뒤에서 부터 가능 + +```python +from tensorflow.keras.preprocessing import sequence + +X_train_seq = sequence.pad_sequences(X_train, maxlen=120) +X_test_seq = sequence.pad_sequences(X_test, maxlen=120) +``` + +- 각 단어 데이터는 라벨 인코딩 상태이므로 feature는 1이된다. +- RNN 학습을 위해 input_shape을 맞춰주기 : (samples, timesteps, feature) + +```python +X_train_seq = X_train_seq.reshape(8982, 120, 1) +X_test_seq = X_test_seq.reshape(2246, 120, 1) + +X_train_seq.shape, X_test_seq.shape +``` + +``` +((8982, 120, 1), (2246, 120, 1)) +``` + +## SimpleRNN 모델링 + +- 현재 한개의 feature는 단어의 빈도수를 의미하므로, 빈도수만을 이용해서 예측하는 RNN 모델을 설계하게 되는것이다 +- 다수 입력(input 120) 단일 출력(output 1) 형태 + +```python +from tensorflow.keras import Sequential +from tensorflow.keras.layers import InputLayer, Dense, SimpleRNN +from tensorflow.keras.optimizers import Adam + +model = Sequential() +model.add(InputLayer(input_shape=(120, 1))) +model.add(SimpleRNN(128))model.add(Dense(46, activation='softmax')) + +model.compile(loss='sparse_categorical_crossentropy', + optimizer=Adam(learning_rate=0.001), + metrics=['acc']) +``` + +```python +history = model.fit(X_train_seq, y_train, validation_split=0.2, batch_size=128, epochs=20) +``` + +```python +plt.figure(figsize=(15, 5)) +plt.plot(history.history['acc'], label='train_acc') +plt.plot(history.history['val_acc'], label='val_acc') +plt.legend() +plt.show() +``` + + + +데이터가 빈도수를 나타내는 feature만 존재하긴 하지만, SimpleRNN으로는 높은 성능을 얻기 힘들다는 것을 확인 + +## LSTM 모델 학습 + +- 다수 입력(input 120) 단일 출력(output 1) +- LSTM을 여러 층으로 쌓기 위해서는 이전 LSTM층이 **다수입력 다수출력 상태**가 되어야한다. +- 그래야 **다음 LSTM층의 input 형태가 이전 LSTM층과 같은 형태**가 되기 때문이다 +- LSTM, RNN층에서 순환할때마다 출력값을 만들어주는 (다수입력 다수출력 형태) **return_sequences=True** 옵션을 사용해야 한다. + +```python +from tensorflow.keras.layers import LSTM, GRU + +model2 = Sequential() +model2.add(InputLayer(input_shape=(120, 1))) +model2.add(LSTM(128, return_sequences=True)) +model2.add(LSTM(128))model2.add(Dense(46, activation='softmax')) + +model2.compile(loss='sparse_categorical_crossentropy', + optimizer=Adam(learning_rate=0.001), + metrics=['acc']) +``` + +```python +history2 = model2.fit(X_train_seq, y_train, validation_split=0.2, batch_size=128, epochs=20) +``` + +```python +plt.figure(figsize=(15, 5)) +plt.plot(history2.history['acc'], label='train_acc') +plt.plot(history2.history['val_acc'], label='val_acc') +plt.legend() +plt.show() +``` + + + +## 워드 임베딩(Word Embedding) + +- 언어 모델의 성능을 높이는 방법은 모델의 구조를 변경하는 방법과 단어의 표현방법을 고도화 하는방법이 있다, +- 단어의 표현을 **밀집되게 실수형태로 표현하게 하는 방법**론이 워드 임베딩이다. +- 학습을 통해서 **각 단어들의 수치값을 정밀하게** 만드는 방법이다. + +### 데이터 불러올 때 등장 빈도가 낮은 단어 제거하기 + +- num_words=1500 : reuters의 수치데이터 의미는 단어 빈도수 랭크이다. 해당 옵션을 주면 각 기사내용에 1500위까지의 단어만 가져오게 된다. +- 1500위 외의 단어들은 oov 즉, 2값으로 표현된다. + +```python +from tensorflow.keras.layers import Embedding + +(X_train, y_train), (X_test, y_test) = reuters.load_data(num_words=1500) + +# 시퀀스 길이 맞추기 +X_train_seq = sequence.pad_sequences(X_train, maxlen=120) +X_test_seq = sequence.pad_sequences(X_test, maxlen=120) +``` + +### Embedding 추가 GRU 모델 설계 + +- Embedding(사용하는 단어사전의 수, 한 단어를 표현할 특징의 수)을 가장 앞 층에 추가하기 +- 사용하는 단어 사전의 수는 학습에 사용되는 단어의 갯수이다 +- 한 단어를 표현할 특징의 수는 적절한 수로 설정해서 모델이 알아서 특징을 검출하게 해준다 + +```python +embedding_model = Sequential() + +embedding_model.add(Embedding(1500, 50)) +embedding_model.add(GRU(128, return_sequences=True)) +embedding_model.add(GRU(128)) +embedding_model.add(Dense(46, activation='softmax')) +embedding_model.compile(loss='sparse_categorical_crossentropy', + optimizer=Adam(learning_rate=0.001), + metrics=['acc']) +``` + +```python +history3 = embedding_model.fit(X_train_seq, y_train, epochs=20, validation_split=0.2, batch_size=128) +``` + +```python +plt.figure(figsize=(15, 5)) +plt.plot(history3.history['acc'], label='train_acc') +plt.plot(history3.history['val_acc'], label='val_acc') +plt.legend() +plt.show() +``` + + \ No newline at end of file diff --git "a/_posts/2024-03-02-aws02_EC2, EBS, ELB \353\202\264\354\232\251 \354\240\225\353\246\254.md" "b/_posts/2024-03-02-aws02_EC2, EBS, ELB \353\202\264\354\232\251 \354\240\225\353\246\254.md" new file mode 100644 index 000000000000..07e44df76aeb --- /dev/null +++ "b/_posts/2024-03-02-aws02_EC2, EBS, ELB \353\202\264\354\232\251 \354\240\225\353\246\254.md" @@ -0,0 +1,110 @@ +--- +layout: single +title: "EC2, EBS, ELB 뜻 정리" +categories: [AWS] +tag: [AWS, EC2, EBS, ELB] +toc: true +toc_sticky: true +post-header: false + +--- + +Elastic Compute Cloud의 약자로 가장 큰 장점인 클라우드라는 공간에서 크기가 유연하게 변경되는 기능을 제공한다. + +예측할 수 없는 데이터 처리 및 계산이 불가능한 크기의 데이터가 밀려오는 상황에 사용량이 많아지면 그만큼 늘리면 되고 반대로 적어지면 또 그만큼 줄여주는 Auto Scaling 가능 + +## EC2의 다양한 지불 방법 + +On-demand + +- 시간 단위로 가격이 고정되어 있음. +- 시간당 정해진 비용을 지불하며 편하게 사용할 수 있고, 소프트웨어 및 서버 개발시 최초로 EC2인스턴스 deploy할 때에 종종 쓰임 +- 또는 사용 기간을 미리 알 수 없는 경우에 유용. + +Reverse + +- 한정된 EC2용량 사용 가능, 1~3년 동안 시간별로 할인 적용 받을 수 있다. +- 선불로 특정한 금액을 지불할 경우 추가적으로 지정되는 컴퓨팅 시스템을 사용할 수 있음. +- On-demand보다 조금 저렵한 가격에 사용할 수 있는 장점이 있지만, On-demand와 달리 크기를 늘리고 줄이는 기능이 없고 특정한 사이즈에 정해지기 때문에 가격이 저렴한 것 같음. +- 무언가를 개발할 때 요구사항이 자주 반복되지 않거나 개발 시간에 대해 예측이 가능하다면 해당 방식 유용 + +Spot + +- 경매와 같이 입찰 가격 적용. 가장 큰 할인률을 적용받으며 특히 인스턴스의 시작과 끝기간이 전혀 중요하지 않을 때 매우 유용 + +## EBS (Elastic Block Storage) + +저장 공간이 생성되어지며 EC2 인스턴스에 부착된다. + +디스크 볼륨 위에 File System이 생성된다. 따라서, EC2인스턴스 접근 뿐만 아니라 파일을 로컬 디스크로 옮기는 작업도 가능하다. + +EBS는 특정 Availability Zone에 생성되므로 Availability Zone을 설정해주어야 함. + +- Availability Zone은 AZ라고도 종종 불리며 하나의 Region 안에 여러개의 AZ가 존재할 수 있음. +- 중심부로부터 복사본들이 존재하여 한쪽 서버가 망가지거나 셧다운되었을 경우 AZ 백업을 통해 서비스 제공을 가능하게 해주는 일종의 Disaster Recovery가 가능. + +## EBS 볼륨 타입 + +< SSD > + +1. General Purpose SSD (GP2) + - 최대 10K IOPS를 지원하며 1GB당 3IOPS 속도가 나옴. + - SSD중에 가장 보편적으로 사용됨. +2. Provisioned IOPS SSD (IO1) + - 극도의 I/O률을 요구하는(ex. 매우 큰 DB관리) 환경에서 주로 사용됨. 10K이상의 IOPS를 지원함 + - 매우 큰 빅데이터 분석 시에도 사용 가능 + +< Magnetic / HDD > + +1. Throughput Optimized HDD (ST1) + - 빅데이터 Datawarehouse, Log 프로세싱시 주로 사용 (boot volume으로 사용 불가능) + - 운영체제를 가지고 있을 수 없음 +2. CDD HDD (SC1) + - 파일 서버와 같이 드문 입출력 즉, 드문 volume 접근 시 주로 사용 + - boot volume으로는 사용 불가능하나 비용이 매우 저렴함 + - 오랫동안 보관해도 괜찮은 데이터들을 처리하는 용도로 쓰기 좋음 +3. Magnetic (Standard) + - 디스크 1GB당 가장 싼 비용을 자랑함. + - boot volume으로 사용 가능. + +## ELB (Elastic Load Balancers) + +수많은 서버의 흐름을 균형있게 배분, 흘려보내는데 중추적인 역할을 함 + +하나의 서버로 트래픽이 몰리는 병목현상을 방지 + +Unhealth instance → Healthy instance + +- EC2 인스턴스는 예상치 못한 이유로 갑자기 셧다운되거나 시간초과 등이 발생하는 unhealthy상태가 됨. 이러한 트래픽 상태를 해결할 수 있음. + +### 1. Application Load Balancer + +OSI Layer7에서 작동됨 (가장 바깥부분) + +- HTTP, HTTPS와 같은 트래픽의 load balancing에 가장 적합함. +- 별도의 라우팅 설정을 통하여 특정 서버로 request를 보낼 수 있음. + + → ELB가 임의로 하지 않고 커스터마이즈인 라우팅이 가능하다는 뜻. + + +### 2. Network Load Balancer + +OSI Layer4에서 작동됨 (Transport Layer). TCP 트래픽을 정리하는데 적합함. + +- 극도의 performance가 요구되는 TCP traffic에서 적합함 +- 초당 수백만개의 request를 아주 미세한 delay로 처리가 가능하다. +- Google, Naver같이 큰 서버에서 적합할듯 하다. + +### 3. Classic Load Balancer + +Legacy ELB라고도 함. (거의 쓰이지 않음) + +- Layer7의 HTTP/HTTPS 라우팅 기능 지원 +- Layer4의 TCP 트래픽 라우팅 기능도 지원 + +## Route 53 + +AWS에서 제공하는 DNS 서비스이다. + +- EC2 instance, S3 Bucket, Load Balancer 등 사용하는 서버의 이름을 부여할 수 있다. +- 도메인 주소를 생성하고 구매하여 위의 3가지 서버로 연결시켜줄 수 있다. \ No newline at end of file diff --git "a/_posts/2024-03-13-aws01_IAM \354\240\225\353\246\254.md" "b/_posts/2024-03-13-aws01_IAM \354\240\225\353\246\254.md" new file mode 100644 index 000000000000..ffd117d89a73 --- /dev/null +++ "b/_posts/2024-03-13-aws01_IAM \354\240\225\353\246\254.md" @@ -0,0 +1,170 @@ +--- +layout: single +title: "IAM 기본 정리" +categories: [AWS] +tag: [AWS, IAM] +toc: true +toc_sticky: true +post-header: false + +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 8px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + + .output_prompt { + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; + -webkit-overflow-scrolling: touch; + padding: 0.8rem; + margin-top: 0; + margin-bottom: 15px; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: $code-text-color; + border: solid 1px $border-color; + border-radius: 0.3rem; + word-break: normal; + white-space: pre; + } + + .dataframe tbody tr th:only-of-type { + vertical-align: middle; + } + + .dataframe tbody tr th { + vertical-align: top; + } + + .dataframe thead th { + text-align: center !important; + padding: 8px; + } + + .page__content p { + margin: 0 0 0px !important; + } + + .page__content p > strong { + font-size: 0.8rem !important; + } + + </style> +</head> + +## IAM 이란? + +**유저를 관리하고 접근 레벨 및 권한에 대한 관리** + +- 접근키(Access Key), 비밀키(Secret Access Key) +- 매우 세밀한 접근 권한 부여 기능 (Granular Permission) +- 비밀번호를 수시로 변경 가능하게 해줌 +- Multi-Factor Authentication(다중 인증) 가능 + +- 그룹(Group) +- 유저(User) +- 역할(Role): 유저와 의미는 비슷하나 역할에 하나 혹은 다수의 정책을 지정할 수 있음, 유저마다 다양한 정책을 부여 +- 정책(Policy) : 주로 JSON 형태로 되어 있는 다큐먼트, 세밀한 접근 권한을 일일히 설정하여 하나의 정책 다큐먼트를 만들 수 있음. + - 정책은 그룹, 역할에 추가시킬 수 있다. 유저에게 직접적으로 정책을 줄 수도 있지만 사용자 그룹에 대해서 줄수도 있다. + - 하나의 그룹 안에 다수의 유저가 존재 가능하다. + - 그룹에 역할 혹은 정책을 추가시키게 되면 그 안의 모든 유저에게 영향이 간다. + +- IAM은 유니버셜함 → 지역 변경이 필요 없음. + +### User 사용자 생성 + +IAM > 액세스 관리 > 사용자 생성 클릭 후 사용자 이름 입력 + + + +자동 생성된 암호 선택 : 사용자 생성 후 자동 생성된 암호를 csv로 다운로드 후 닫기버튼 눌러야함. (닫은 후에는 다시 확인 불가) + +2단계 : 권한 설정 + + + +권한 옵션을 선택하여 생성하는 사용자를 기존 그룹에 추가할 수 있다. + +### Group 그룹 + + + +생성된 그룹 정보에서 사용자 및 권한을 추가할 수 있다. + +### Role 역할 생성 + + + +역할 유형을 선택(AWS 서비스)을 하고, 사용할 서비스를 선택한다. + +### Policy 정책 생성 + +정책 생성 > 정책에 적용되는 서비스를 선택 + + + +여기서 액세스 수준을 선택할 수 있는데 읽기, 쓰기 모두 선택함. + +아래에 리소스를 모두 선택할 수 있고, 특정 리소스를 선택할 수도 있다. + + + +권한 목록에서 생성한 권한을 클릭 후, JSON 형태로 확인/수정 가능하다. + + + +## IAM 정책 시뮬레이터 + +개발환경에서 실제 환경 프로덕션으로 빌드하기 전에 IAM 정책 또는 Role들이 잘 작동되는지 테스트하기 위한 툴 + +IAM과 관련된 문제들을 디버깅하기에 최적화된 툴, 이미 실제로 유저에 부여된 다양한 정책들도 테스트 가능 + +IAM > 대시보드 > 오른쪽 메뉴에 정책 시뮬레이터 클릭 + +왼쪽 메뉴의 드롭다운에서 시뮬레이션 할 대상을 선택 (Users, Groups, Roles 선택 가능) + + + +Select Service : 시뮬레이션 할 서비스 선택한다. + + + +Select actions : 시뮬레이션할 기능들을 선택한다. (나는 Select All 클릭) + + + +Run Simulation을 실행하면 시뮬레이션 결과로 권한이 access / denied로 나옴 + + + +시뮬레이션 한 유저에 AmazonDynamoDBReadOnlyAccess 추가 후 다시 테스트 + + \ No newline at end of file diff --git a/_posts/2024-04-25-aws03_X-Forwared-For.md b/_posts/2024-04-25-aws03_X-Forwared-For.md new file mode 100644 index 000000000000..9609f718085d --- /dev/null +++ b/_posts/2024-04-25-aws03_X-Forwared-For.md @@ -0,0 +1,31 @@ +--- +layout: single +title: "X-Forwared-For란?" +categories: [AWS] +tag: [AWS] +toc: true +toc_sticky: true +post-header: false + +--- + +### X-Forwared-For 헤더 + +단순히 포트만 열어두고 Application 서비스를 올리는 경우가 아닌 NGINX 및 AWS CloudFront 또는 Application Load Balancer 같이 앞단에 프록시 서버를 두는 경우가 있다. + +이러한 경우 앱 서버(WAS)는 유저의 request가 리버스 프록시를 통해 접속하는데, 이 경우 서버의 접속 로그에 찍히는 IP는 리버스 프록시 서버의 IP(Private IP)만 가져온다. 출처를 알 수 없음 + + + +이러한 문제를 해결하기 위해 표준헤더인 X-Forwarded-For가 등장하게 되고, HTTP 헤더에 타고 있는 X-Forwarded-For를 통해 프록시 및 로드밸런서 아이피를 기록한다. + +> X-Forwared-For: <client>, <proxy1>, <proxy2> +> + +HTTP HEAD에 타고 있는 형태로 구성되며, client IP → Proxy Server1 → Proxy Server2 → … 순차적으로 추가되는 방식으로 구성 + +이 설정은 각 프록시 서버별로 세팅이 필요하거나 자동으로 해더에 같이 넣어줌 + +- AWS ELB → 자동으로 X-Forwarded-For 헤더가 추가되거나 새롭게 생성 + +즉, 순차적으로 서버를 넘어가면서 받은 아이피를 기록하는 해더에서 실제 출처 IP를 찾을 수 있다. \ No newline at end of file diff --git "a/_posts/2024-05-02-aws4_AWS VPC\354\231\200 Public,Private Subnet \354\203\235\354\204\261.md" "b/_posts/2024-05-02-aws4_AWS VPC\354\231\200 Public,Private Subnet \354\203\235\354\204\261.md" new file mode 100644 index 000000000000..26007c5d3ee9 --- /dev/null +++ "b/_posts/2024-05-02-aws4_AWS VPC\354\231\200 Public,Private Subnet \354\203\235\354\204\261.md" @@ -0,0 +1,282 @@ +--- +layout: single +title: "AWS VPC와 Public,Private Subnet 생성" +categories: [AWS] +tag: [AWS, EC2, VPC] +toc: true +toc_sticky: true +post-header: false + +--- + +## 구축 순서 + +VPC 생성 → 서브넷 생성 → 인터넷 게이트웨이 생성 → 퍼블릭 라우팅 → 라우팅 테이블 생성 및 설정 + + + +## VPC (Virtual Private Cloud) + +- AWS 클라우드의 프라이빗 가상 네트워크 +- 보통 AWS 리전 및 가용 영역의 고가용성을 기반으로 구축한다. +- VPC는 한 리전 내에 상주하며, 여러 가용영역에 걸쳐 사용 가능하다. +- VPC는 네트워크 구성을 완벽하게 제어할 수 있도록 허용한다. +- VPC 내부의 리소스를 격리하고 노출할 수 있는 기능이 있다, (서브넷, 액세스 제어 목록 정의, 라우팅 규칙 등) +- 여러 계층의 보안 제어 기능을 제공한다. +- 특정 인터넷 및 내부 트래픽을 허용 및 거부할 수 있는 기능이 있다. +- 다른 AWS 서비스를 VPC에 배포할 수 있다. + +## 1. VPC 생성 + +VPC 이름은 이름-리전-vpc로 작성하였다. + +이후, IPv4 CIDR 블록에 생성할 VPC IP 대역대를 입력 + + + +## 2. Public subnet 생성 + +### Subnet이란? + +VPC를 나누는 데 사용된다. VPC안에 나누어진 부분 네트워크라고 할 수 있다. + +보통 VPC를 IP로 구분해 나눈 후에 다중 AZ(가용영역)에 걸쳐 서브넷을 구성한다. + +- 퍼블릭 서브넷 + - 인터넷에 직접 엑세스가 가능한 서브넷이다. + - 인터넷 게이트웨이를 VPC에 연결하고 퍼블릭 서브넷의 라우팅 테이블을 업데이트하여 로컬이 아닌 트래픽을 인터넷 게이트웨이로 보내면 퍼블릭 서브넷처럼 사용할 수 있다. +- 프라이빗 서브넷 + - 인터넷에 직접 액세스가 불가능한 서브넷이다. + - 보통 회사 내부 서버나 프로그램들은 프라이빗 서브넷에 위치시켜 인터넷을 통해 아무나 접근이 불가능하게 한다. + +### Public Subnet을 각각 A,C 가용영역에 생성해보기 + + + +서브넷을 생성할 VPC를 목록에서 선택하고, 서브넷 이름은 이름-public-subnet-가용영역으로 생성하였다. + + + +첫번째로, 가용영역 A에 서브넷을 생성해준다. + +IPv4 CIDR 블록에는 선택한 VPC의 IP범위 내, 생성할 서브넷의 IP 대역대를 입력한다. + + + +새 서브넷 추가를 클릭하여 가용영역 C에 서브넷을 하나 더 추가해준다. + + + +## 3. Internet Gateway 생성 + +Internet Gateway란 + +- VPC에 부착되어, VPC와 인터넷 간에 통신할 수 있게 해주는 출입문 역할을 한다. +- 방금 만든 서브넷들이 퍼블릭 서브넷 역할을 하게끔 해주려면, 인터넷으로 접속할 수 있어야 하기 때문이다. + + + +이제 방금 만든 인터넷 게이트웨이를 선택해서 VPC와 연결을 해준다. + + + + + + + +## 4. Public 전용 라우팅 테이블 생성 + +Routing Table 이란 + +- IP 주소에 트래픽 라우팅 경로를 정의하여 Subnet 안팎으로 나가는 트래픽에 대한 라우팅 경로 설정 기능을 수행한다. +- 인터넷을 통해 요청이 들어왔을 때, 요청이 들어올 경로 안내를 하는 네비게이션 역할을 한다. + +라우팅 테이블이 사용할 VPC를 목록에서 선택 후 생성해준다. + + + +그다음, 라우팅 테이블 목록에서 라우팅 테이블을 체크 후 ‘서브넷 연결 편집’ 메뉴를 통해 서브넷과 연결해준다. + + + +‘서브넷 연결 편집’은 해당 라우팅 테이블이 어떤 서브넷에 적용이 되는지 설정해주는 곳이다. + +퍼블릭 서브넷 역할을 하게 할 라우팅 테이블이므로, 퍼블릭 역할을 하게 될 서브넷들만 선택한다. + + + +이제 어떤 라우팅을 해줄 것인지 설정해주면 된다. + +방금 등록한 라우팅 테이블을 선택 후 ‘라우팅 편집 버튼’을 클릭하여 설정해준다. + + + +local : 현재 기본 설정되어 있는 라우팅, VPC 대역대에 있는 IP들 (즉, VPC 내부의 Private IP)들이 서로 알고있게끔 하는 기본 설정이다. + +라우팅을 추가하여 대상에는 0.0.0.0/0을 입력, 만든 Internet Gateway로 설정한다. + +즉, 위치 무관 아무나 들어올 수 있게 추가한다는 의미 + + + +이제 VPC에 priavte subnet을 구축해보자 + +프라이빗 서브넷 생성 → 프라이빗 라우팅 테이블 생성 및 설정 순으로 진행 + +## 5. Private Subnet 생성 + +이전과 비슷한 방식으로 가용영역 A,C에 생성해준다. + +이름-private-subnet-가용영역 으로 생성하고, 서브넷의 IP대역대를 설정해준다. + + + + + +## 6. Private 라우팅 테이블 생성 + +라우팅 테이블 이름과 사용될 VPC를 선택하고 생성해준다. + + + +‘서브넷 연결 편집’ 메뉴에서 방금 만든 private subnet들을 선택하여 등록해준다. + + + +private subnet에 위치하게 될 리소스들은 private IP만 가지기 때문에 내부가 아닌 외부의 리소스들과 통신을 할 수 없게 된다. + +### NAT 인스턴스 생성 + +private IP를 public IP로 변환해 private IP는 감추고, 통신을 할 수 있도록 하게 해준다. + +NAT Gateway는 비용이 비싸므로 NAT역할을 할 수 있는 NAT Instance를 생성하여 EC2의 t2.micro 비용으로 NAT 역할을 하는 서버를 만들것이다. + +프라이빗 라우팅 테이블에 이 NAT Instance를 추가하면 private subnet안의 리소드들은 이를 통해 외부 리소스들과 통신할 수 있게 된다. + +## 7. EC2(NAT Instance) 생성 + +- private subnet 내의 웹 서버들이 외부로 통신을 하기 위해 사용하는 서버 +- 보통 NAT서버를 public subnet안에 구축하여 NAT를 통해 외부로 통신함 + +AMI 선택 + +AMI > ‘nat’로 검색 > 아무버전이나 선택 > AMI로 인스턴스 시작 + + + +인스턴스 유형 : t2.micro 선택 + +VPC : 내가 만든 vpc 선택 + +서브넷 : public subnet - a 선택 + +스토리지 : default + + + +새로운 keypair 생성 + +보안 그룹 추가 : 내 PC에서만 SSH 접속이 가능하도록 설정 + +마지막으로, NAT는 소스/대상 확인을 하면 안됩니다 + +그렇기 때문에 방금 생성한 nat instance 서버를 체크하고 작업-네트워킹-소스/대상 확인 변경 클릭 + +소스/대상 확인의 ‘중지’를 체크하고 저장하기 + + + +## 8. 탄력적 IP 주소 할당 + +- 탄력적 IP 주소는 인터넷에서 연결 가능한 퍼블릭 IPv4 주소 이다. +- AWS 계정에 할당되며 릴리스할 때까지 할당된 상태로 유지된다. +- 탄력적 IP주소를 생성해 방금 만든 인스턴스에 연결해주면 해당 인스턴스 IP는 고정 IP주소가 된다. + + + +생성할 때, 맨 아래에 Name 태그를 하나 추가해줘서 식별해주기 + + + +### 탄력적 IP주소 연결 + +EIP를 생성했으면 해당 EIP를 사용할 인스턴스를 연결해야 한다. + +해당 EIP를 체크해주고, 작업 매뉴에서 탄력적 IP 주소 연결 실행 + + + +연결할 인스턴스를 선택하고 연결 버튼 클릭. + +주의) EIP는 AWS에게 IP주소를 빌려오는 개념이기 때문에 EIP에 인스턴스를 연결해주지 않으면 오히려 요금이 부과되는 형태이다. 만약 EC2 인스턴스를 릴리즈(종료)할려면, 꼭 EIP도 같이 릴리즈하기 + + + +이제 NAT Instance 생성 및 설정이 완료되었으니, 0.0.0.0/0 트래픽을 NAT를 통해 들어올 수 있도록 바꾸면 된다. + +라우틴 테이블 편집하여 대상을 0.0.0.0/0, 인스턴스 선택 → nat instance를 선택한다. + + + +이제 private subnet a에 EC2 인스턴스 생성하여 웹서버를 구축해준다. + +기존에 가지고 있던 간단한 AMI로 생성해준다. + +keypair는 nat instance의 keypair로 사용. + +private subnet으로 네트워크 설정을 했으므로 아래와 같이 퍼블릭 IP주소는 할당이 안되게 잘 생성해주자. + + + +## 9. ALB & Target Group 생성 + +### ALB(Application Load Balancer) + +- 둘 이상의 가용 영역에서 EC2 인스턴스, 컨테이너, IP주소 등 여러 대상에 걸쳐 수신되는 트래픽을 자동으로 분산하는 서비스 +- LB는 등록된 대상의 상태를 모니터링하며, 상태가 양호한 대상으로만 트래픽을 라우팅 해준다. +- LB에는 여러 종류가 있음(Application LB, Network LB, Gateway LB, Classic LB) +- 특히, ALB는 HTTP의 URL, FTP의 파일명, 쿠키 정보 등을 분석해 더 정교한 로드 밸런싱이 가능한 서비스이다. + +EC2 - 로드 밸런싱 - 로드밸런서 클릭 + + + +[Application Load Balancer 선택] + +이름 : tester-alb + +체계 : 인터넷 경계 + +VPC : tester-seoul-vpc + +가용영역 : a,c체크 및 public-subnet-a,c 선택 + +보안그룹 : 인바인드 규칙 0.0.0.0/0 80port + + + + + +리스너 및 라우팅 + +간단하게 http 통신을 해볼것이기 때문에 80포트 사용(spring, node.js 등의 웹서버를 올리려면 해당 웹서버에서 사용하는 포트로 지정) + + + +대상 그룹 생성 클릭하여 대상 그룹 지정해주기 + +ALB 대상은 인스턴스이므로 인스턴스 선택 + +프로토콜 : 포트 지정 + + + + + +ALB가 해당 옵션으로 타겟의 healthy/unhealty 상태를 검사함. + + + +생성을 클릭하면 아래와 같은 화면에서 등록할 대상 즉, 인스턴스를 선택할 수 있다. + + \ No newline at end of file diff --git "a/_posts/2024-05-04-OLAP\354\231\200 OLTP.md" "b/_posts/2024-05-04-OLAP\354\231\200 OLTP.md" new file mode 100644 index 000000000000..1f8a10e1942f --- /dev/null +++ "b/_posts/2024-05-04-OLAP\354\231\200 OLTP.md" @@ -0,0 +1,80 @@ +--- +layout: single +title: "OLAP와 OLTP" +categories: [CS] +tag: [cs] +toc: true +toc_sticky: true +post-header: false + +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + </style> +</head> + +## 1. OLTP (Online Transaction Processing) + +### OLTP 정의 + +- ‘운영’계 데이터 및 데이터를 처리하는 방법을 의미한다. +- 복수의 사용자 PC에서 발생되는 트랜잭션을 DB서버가 처리, 그 결과를 요청한 사용자에게 실시간으로 결과값을 되돌려주는 과정 +- 1개의 트랜잭션에서 발생되는 INSERT, UPDATE, DELETE를 무결성 보장하여 처리하고 결과를 SELECT하는 과정 + +### OLTP의 특징 + +- 과거에는 주로 RDBMS 기반의 시스템을 근간으로 했으나 NoSQL도 운영계 데이터의 성격을 띤다면 OLTP로 분류 가능 +- 현재 데이터가 처리가 얼마나 정확하고 무결한지가 중요 +- 실질적인 데이터의 저장, 삭제, 수정 등의 작업을 의미 +- 비교적 작은 규모의 트랜잭션들로 구성 +- CRUD와 관련된 쿼리들이 주를 이룸 +- 전통적인 DBMS들이 주로 사용됨 + +## 2. OLAP (Online Analytical Processing) + +### OLAP의 정의 + +- ‘분석’계 데이터 및 데이터를 처리하는 방법을 의미한다. +- 데이터 웨어하우스(DW), DB에 저장되어 있는 데이터를 분석, 사용자에게 유의미한 정보를 제공해주는 처리 방법이다. +- 기존에 저장되어 있는 데이터를 사용자의 요구와 목적에 맞게 분석하여 정보를 제공하는 개념 + +### OLAP의 특징 + +- 분석을 통해 BI(Business Intelligence)와 연계하여 특정 지표 추출, 리포트 생산, 의사 결정에 도움 +- 이미 저장된 데이터를 바탕으로 어떤 정보를 제공하는지가 중요 +- 데이터가 무결, 정확하다는 전재 하에 정보를 어떤 식으로 표현하고 제공하는지를 의미 +- 대용량 데이터를 취급 +- 통계/집계 등의 복잡한 쿼리들이 주를 이룸 +- DW(Data Warehouse)를 구축 +- 데이터가 어느정도 중복이 되더라도 별 상관이 없으며 많은 데이터 수집이 필요 + +## OLTP와 OLAP 비교 + +| 구분 | OLTP | OLAP | +| --- | --- | --- | +| 목적 | 비즈니스 활동 지원 | 비즈니스 활동에 대한 평가, 분석 | +| 주 트랜잭션 형태 | SELECT, INSERT, UPDATE, DELETE | SELECT | +| 속도 | 수 초 이내 | 수 초 이상 수 분 이내 | +| 데이터 표현 시간 | 실시간 | 과거 | +| 관리 단위 | 테이블 | 분석된 정보 | +| 최적화 방법 | 트랜잭션 효율화, 무결성 극대화 | 조회 속도, 정보의 가치, 편의성 | +| 데이터의 특성 | 트랜잭션 중심 | 정보 중심 | +| 중요점 | 데이터의 정확도, 무결성 | 결과의 속도, 표현 방식 | +| 활용자 | 운영자 | 분석가, 의사결정자1 | + +- OLTP와 OLAP 중 가장 선행되어야 할 부분은 OLTP (데이터 처리 과정 및 데이터 자체의 무결성 중요) +- OLTP가 원천 데이터의 제공처이며 ETL작업을 통해 OLAP에 데이터를 제공 \ No newline at end of file diff --git "a/_posts/2024-05-10-RDS\354\235\230 ElastiCache.md" "b/_posts/2024-05-10-RDS\354\235\230 ElastiCache.md" new file mode 100644 index 000000000000..8d303edc99a0 --- /dev/null +++ "b/_posts/2024-05-10-RDS\354\235\230 ElastiCache.md" @@ -0,0 +1,45 @@ +--- +layout: single +title: "RDS의 ElastiCache" +categories: [AWS] +tag: [AWS, RDS] +toc: true +toc_sticky: true +post-header: false + +--- + +## ElastiCache + +- 클라두으 내에서 In-memory 캐시를 만들어줌 +- 데이터베이스에서 데이터를 읽어오는것이 아니라 캐시에서 빠른 속도로 데이터를 읽어옴 + - 수백, 수천만 개의 프로세싱을 동시다발적으로 처리할 경우 큰 차이가 생김 + - 종종 불러오는 레코드들은 예를 들면 SNS, 네이버 실시간 검색어 Top10 등 많은 사람들에 의해서 읽혀지는 데이터들을 캐시에 넣음으로써 빠른 로딩을 가능하게 함 +- Read-Heavy 어플리케이션에서 상당한 Latency 감소 효과가 있음 +- 초반 어플리케이션 개발 및 테스트 용도로는 적합하지 않음 +- Elasti Cache는 Memcached와 Redis가 존재한다. + +## Memcached + +- Object 캐시 시스템으로 잘 알려져 있음 +- ElastiCache는 Memcached의 프로토콜을 디폴트로 따름 +- EC2 Auto Scaling처럼 데이터 처리 사용량에 따라 캐시의 크기가 커졌다 작아졌다 가능함 +- Memcached는 오픈 소스이다. + +### Memcached는 이럴때 사용하면 좋다! + +1. 가장 단순한 캐싱 모델이 필요한가요? Yes +2. Object caching이 주된 모기적인가요? Yes +3. 캐시 크기를 마음대로 scaling하기를 원하나요? Yes + +## Redis + +- Key-Value, Set, List와 같은 형태의 데이터를 In-Memory에 저장 가능함 +- 오픈 소스이다. +- Multi-AZ 기능을 지원한다. (Disaster Recovery 기능) + +### Redis는 이럴때 사용하면 좋다! + +1. List, Set과 같은 데이터셋을 사용하나요? Yes +2. 리더보드처럼 데이터셋의 랭킹을 정렬하는 용도가 필요한가요? Yes +3. Multi AZ기능이 필요하나요? Yes \ No newline at end of file diff --git "a/_posts/2024-05-10-aws5_AWS RDS \353\260\261\354\227\205.md" "b/_posts/2024-05-10-aws5_AWS RDS \353\260\261\354\227\205.md" new file mode 100644 index 000000000000..2f5d652b8edd --- /dev/null +++ "b/_posts/2024-05-10-aws5_AWS RDS \353\260\261\354\227\205.md" @@ -0,0 +1,40 @@ +--- +layout: single +title: "AWS RDS 백업" +categories: [AWS] +tag: [AWS, RDS] +toc: true +toc_sticky: true +post-header: false + +--- + +AWS RDS에는 두 가지 백업 기능이 존재한다. + +- Automated Backups (자동 백업) +- DB Snapshots (데이터베이스 스냅샷) + +## Automated Backups(AB) - 자동 백업 + +1. Retention Period(1 ~ 35일) 안의 어떤 시간으로 돌아가게 할 수 있음 + - 현 시점부터 35일 전까지가 Retenntion Period에 해당 +2. AB는 그날 생성된 스냅샷과 Transaction logs(TL)을 참고함 + - AB는 복원을 대비하기 위해 매일 스냅샷과 트랜잭션 로그를 끊임없이 생성함 + - 유저가 고른 날짜로 돌아가길 원한다면 그 날짜에 해당하는 스냅샷과 트랜잭션 로그를 참조한다. +3. 디폴트로 AB기능이 설정되어 있으며 백업 정보는 S3에 저장됨 + - RDS 인스턴스 크기만큼에 해당되는 용량까지만 무료로 사용 가능 +4. AB동안 약간의 I/O suspension이 존재할 수 있음(체감상 크게 느껴지지는 않지만 어느정도 딜레이 존재) + +## DB Snapshots - 데이터베이스 스냅샷 + +1. 주로 사용자에 의해 실행됨 +2. 원본 RDS Instance를 삭제해도 스냅샷은 존재함 + - 원본 RDS 인스턴스를 삭제해도 스냅샷은 S3에 존재함 + - AB 기능은 원본 인스턴스를 삭제할 시 사라짐 + +## 데이터베이스 백업했을 때 + + + +- 전혀 다른 RDS 인스턴스가 생성된다. +- 전혀 다른 RDS 엔드포인트가 생성된다. \ No newline at end of file diff --git "a/_posts/2024-05-10-aws6_RDS\354\235\230 Multi AZ, Read Replicas.md" "b/_posts/2024-05-10-aws6_RDS\354\235\230 Multi AZ, Read Replicas.md" new file mode 100644 index 000000000000..d5089602a22e --- /dev/null +++ "b/_posts/2024-05-10-aws6_RDS\354\235\230 Multi AZ, Read Replicas.md" @@ -0,0 +1,39 @@ +--- +layout: single +title: "RDS의 Multi AZ, Read Replicas" +categories: [AWS] +tag: [AWS, RDS] +toc: true +toc_sticky: true +post-header: false + +--- + +## Multi AZ + +- 원래 존재하는 RDS DB에 무언가 변화(ex. write)가 생길 때 다른 Availability Zone에 똑같은 복제본이 만들어짐 = Synchronize +- AWS에 의해서 자동으로 관리가 이루어짐 (No admin intervention) +- 원본 RDS DB에 문제가 생길 시 자동으로 다른 AZ의 복제본이 사용됨 (Disaster Recovery) +- 성능 개선을 위해서 사용되지는 않음 → 성능 개선을 기대하기 위해선 Read Replica 사용 + + + +- 3개의 인스턴스가 하나의 프로덕션 RDS DB에 연결되어 있고 쓰기 기능이 실행된다면, +- 현재 RDS 엔드포인트는 ap-northeast-2a 이지만 쓰기 기능이 실행된 후 똑같은 복제본이 다른 AZ(ap-northeast-2b)에 쓰여진다. +- 만약 AZ 2a의 RDS에 문제가 생긴다면 RDS는 자동으로 AZ 2b로 failover를 한다. + +## Read Replica + +- 프로덕션 DB의 읽기 전용 복제본이 생성됨 +- 주로 Read-Heavy DB 작업시 효율성의 극대화를 위해 사용된다 (Scaling) +- Disaster Recovery 용도가 아님 +- 최대 5개의 Read Replica DB가 허용됨 +- Read Replica의 Read Replica 생성이 가능(단, 약간의 Latency 발생) +- 각각의 Read Replica는 자기만의 고유 엔드포인트가 존재한다. +- RDS DB는 IP 주소가 아닌 엔드포인트로 고유 식별을 할 수 있다. + + + +- 3개의 인스턴스가 하나의 메인 프로덕션 RDS에 연결되어 있을 때 쓰기 작업이 실행될 시 read replica에 의해 똑같은 RDS 복제본이 생성된다, +- 그리고 3개의 인스턴스에서 Read Traffic이 일어날 때, 메인 프로덕션 DB로 모두 연결시키는것이 아니라 하나의 EC2 인스턴스를 각각의 Read Replica로 연결시켜줌 +- 따라서, 메인 DB의 워크로드를 현저히 낮출 수 있으며 성능 개선 효과를 누릴 수 있다. \ No newline at end of file diff --git "a/_posts/2024-05-15-aws07_RDS \354\203\235\354\204\261 \353\260\217 EC2 \354\235\270\354\212\244\355\204\264\354\212\244 \354\227\260\352\262\260.md" "b/_posts/2024-05-15-aws07_RDS \354\203\235\354\204\261 \353\260\217 EC2 \354\235\270\354\212\244\355\204\264\354\212\244 \354\227\260\352\262\260.md" new file mode 100644 index 000000000000..858e9cc649d2 --- /dev/null +++ "b/_posts/2024-05-15-aws07_RDS \354\203\235\354\204\261 \353\260\217 EC2 \354\235\270\354\212\244\355\204\264\354\212\244 \354\227\260\352\262\260.md" @@ -0,0 +1,154 @@ +--- +layout: single +title: "RDS 생성 및 EC2 인스턴스 연결하기" +categories: [AWS] +tag: [AWS, RDS] +toc: true +toc_sticky: true +post-header: false + +--- + +## RDS 생성 + +RDS > 데이터베이스 생성 > MySQL로 생성 > 버전은 기본으로 선택(MySQL 8.0.35) + + + +템플릿에서 프로덕션과 개발/테스트 를 선택하면 아래에 Multi AZ 기능을 사용할 수 있다. + +프리티어에서는 사용 불가. + + + +DB인스턴스 이름과 어드민 사용자 이름, 암호를 설정한다. + + + +그다음에 인스턴스 구성을 한다. 프리티어를 선택했기 때문에 버스터블 클래스 이외에는 선택사항이 없다. + +버스터블 클래스란 상황에 따라서 CPU의 성능을 버스트시킬 수 있다는 뜻이다. + + + +스토리지 유형은 범용 SSD(gp2)로 선택한다. 스토리지 유형 목록을 보면 GP3란게 있는데, 이건 추가 비용을 냄으로써 IOPS 처리량을 늘릴 수 있다. + +할당된 스토리지의 크기는 간단한 시스템을 유지하기 위해 20GIB로 설정한다. + +스토리지 자동 조정 활성화란게 있는데 오토 스케일링을 뜻한다. 최대 스토리지 임계값은 디폴트(1000GIB)로 설정해준다. + + + +## EC2 인스턴스 연결 + +그 다음은 컴퓨팅 리소스에 대해서 묻는데, 최근 UI 업데이트를 통해 여기서 직접 EC2 인스턴스를 만들어서 RDS와 바로 연결시킬 수 있다. + + + +여기서 RDS와 연결해줄 EC2 인스턴스를 생성해둔다. + +- 인스턴스명 : aws-learner-rds-ec2-instance, 내가 가지고 있는 기존 키페어(testkey.pem) 선택 +- 보안 그룹 : http 80포트와 ssh 22포트를 위치무관 접속할 수 있게 설정. + +  + +- 고급 세부 정보 > 사용자 데이터 에서 편리한 설정을 위해 EC2 인스턴스가 생성되면서 실행될 스크립트 내용을 넣어준다. +- EC2 인스턴스 생성 후 apache, php, mysql을 yes 옵션으로 설치 후 아파치 실행 +- 간단한 php파일을 아파치 서버 디렉토리 내에 index.php로 저장 +- wget 명령어로 S3내의 connect.php 파일을 다운로드 + +connect.php 파일 내용 + +```php +<?php +$username = "admin"; +$password = "awslearner"; +$hostname = "yourhostnameaddress"; +$dbname = "awslearner"; + +//connection to the database +$dbhandle = mysql_connect($hostname, $username, $password) or die("MySQL에 연결할 수 없습니다"); +echo "MySQL 접속 성공! username - $username, password - $password, host - $hostname<br>"; +$selected = mysql_select_db("$dbname",$dbhandle) or die("MySQL DB 연결 실패... - 다시 시도해보세요!"); +?> +``` + +EC2 인스턴스 생성이 완료되었으면 다시 RDS 생성 화면으로 돌아와서 방금 생성한 EC2 인스턴스를 선택해준다. + + + +VPC는 디폴트로 만들어질것이고, DB 서브넷 그룹은 기존 항목을 선택하거나 자동설정으로 선택해준다. + +VPC 보안 그룹은 선택/생성이 있는데 기존에 생성했던 ssh, http 접근이 가능한 보안그룹으로 선택해줬다. + + + +다음으로 가용영역이 있는데, 지금은 프리티어 용이라 Multi-AZ 기능을 사용할 수 없으므로 선택 권한이 없다. + +추가 구성에서 데이터베이스 포트를 지정할 수 있다. (MySQL default 포트는 3306) + + + +다음은 데이터페이스 암호 인증을 선택할 수 있는 곳이다. 여기선 위에서 부여했던 어드민 사용자, 암호를 가지고 데이터베이스에 접속할 수 있게끔 ‘암호 인증’을 선택해 줬다. + + + +마지막으로 추가구성에서 데이터베이스 옵션에서는 초기 데이터베이스 이름을 설정해주고 나머지는 디폴트로 설정해줬다. + + + +## EC2 인스턴스 확인 + +방금 RDS와 연결한 인스턴스를 퍼블릭 IP주소로 접속해보면 php 화면이 뜬다. + + + +이제 이 EC2 인스턴스에 터미널로 접속해본다. + + + +아까 EC2를 생성할 때 입력했던 구문중 connect.php 파일 내용 확인 + +/var/www/html 폴더에 wget 이동 + + + +- vi connect.php +- hostname 값을 생성한 RDS 인스턴스의 엔드포인트명으로 바꿔준다. + +  + + +수정 한 후 ec2 DNS명/connect.php로 확인해보기 + +확인해보면 아래와 같이 MySQL에 연결할 수 없다고 나온다. + + + +이유는 현재 RDS 보안그룹은 RDS 인스턴스 안에서만 존재하며, + +우리가 웹에서 접속한 EC2 인스턴스에서는 별개의 보안그룹이 존재한다. + +두 개의 서로 다른 클라우드에서 다른 보안 그룹이 존재하므로 현재 EC2에서 RDS로 통신이 불가능한 상태이다. + +## RDS 보안 그룹 확인하기 + +생성한 RDS의 보안 그룹 > 인바운드 규칙 확인 + +그다음, 이 소스를 지우고 아까 EC2 인스턴스가 속해 있는 보안 그룹을 찾아서 연결해준다. + + + +그러면 이렇게 ‘기존 IPv4 CIDR 규칙에 a 참조된 그룹 ID를 지정할 수 없습니다.’ 라는 에러가 뜬다. + +위 에러를 해결하려면, 이 규칙을 삭제하고 다시 똑같이 만들어주면 지정이 된다. + + + +다시 똑같이 만들어 준 후에 저장을 누른다. + +(DB 파라미터 그룹은 따로 정리.) + +다시 /connect.php 화면을 로딩해서 접속 성공 여부를 확인한다. + + \ No newline at end of file diff --git "a/_posts/2024-05-15-aws08_RDS \355\214\214\353\235\274\353\257\270\355\204\260 \352\267\270\353\243\271.md" "b/_posts/2024-05-15-aws08_RDS \355\214\214\353\235\274\353\257\270\355\204\260 \352\267\270\353\243\271.md" new file mode 100644 index 000000000000..7ac610f3d0ce --- /dev/null +++ "b/_posts/2024-05-15-aws08_RDS \355\214\214\353\235\274\353\257\270\355\204\260 \352\267\270\353\243\271.md" @@ -0,0 +1,106 @@ +--- +layout: single +title: "RDS 파라미터 그룹" +categories: [AWS] +tag: [AWS, RDS] +toc: true +toc_sticky: true +post-header: false + +--- + +<head> + <style> + table.dataframe { + white-space: normal; + width: 100%; + height: 240px; + display: block; + overflow: auto; + font-family: Arial, sans-serif; + font-size: 0.9rem; + line-height: 20px; + text-align: center; + border: 0px !important; + } + + table.dataframe th { + text-align: center; + font-weight: bold; + padding: 0px; + } + + table.dataframe td { + text-align: center; + padding: 8px; + } + + table.dataframe tr:hover { + background: #b8d1f3; + } + </style> +</head> + +## RDS 파라미터 그룹 설정 + +데이터베이스 파라미터 그룹은 데이터베이스 구성 방법을 지정해준다. 데이터베이스에 할당할 메모리 등의 리소스 양을 지정할 수 있다. + +또한, DB 인스턴스와 다중 AZ DB 클러스터를 파라미터 그룹과 연결하여 데이터베이스 구성을 관리하는 작업도 지원한다고 한다. + +RDS > 파라미터 그룹에서 새로운 파라미터 그룹을 생성한다. + + + +아래와 같이 설정을 마치고 생성해준다. + +- 파라미터 그룹 이름 입력 +- 엔진 유형 : MySQL Community +- 파라미터 그룹 패밀리 : mysql8.0 +- 유형 : DB Parameter Group + + + +파라미터 그룹을 생성한 후에 해당 파라미터 그룹을 클릭한다. + +편집을 눌러서 파라미터 값을 수정해주면 된다. + + + +1. Character Set (문자 인코딩 설정) + - 기본적으로 utf8은 많이 들어보았지만, 가변3바이트인 utf8에는 이모지 문자가 입력이 되지 않는다. + - 따라서, 4바이트를 사용하는 utf8mb4로 설정한다. +2. Time Zone(시간 설정) +3. Collation (데이터의 정렬 기준을 위한 설정) + +### 1. Character Set 설정 + +- character_set_client, character_set_connection, character_set_database, character_set_server, character_set_filesystem, character_set_results = utf8mb4 +- MySQL의 character에 대한 정리 + + +| 파라미터 | 설명 | +| ---| --- | +| **character_set_client** | - MySQL Client의 Default Character set <br> - Client로부터 전송되는 쿼리문장의 문자집합을 말함<br> - 각 커넥션에서 임의의 문자집합으로 변경해서 사용 가능 | +| **character_set_connection** | - MySQL Server가 Client로부터 전달받은 쿼리 문장에서 인트로듀서(Introducer)가 없는 리터럴(literal) 또는 숫자(number) 값을 문자(String)열로 변환할 때 사용하는 Character set <br> - 각 커넥션에서 임의의 문자집합으로 변경해서 사용 가능 | +| **character_set_database** | - MySQL Database의 Default Character set <br> - 이 변수가 지정되지 않았다면 character_set_server와 같은 값을 가짐 <br> - 각 DB, TABLE, COLUMN은 기본 문자셋과 관계없이 개별적인 문자집합을 가질 수 있으며 DB를 생성할 때 아무런 문자집합이 명시되지 않았다면, 이 변수의 값이 기본값으로 사용됨 | +| **character_set_filesystem** | - LOAD DATA INFILE ... 또는 SELECT ... INTO OUTFILE 문장이 실행될 때 파일의 읽고 쓰기에 사용되는 Character set <br> - 데이터 파일의 내용을 읽을 때 사용하는 문자집합이 아닌, 파일의 이름을 찾을 때 사용하는 문자 집합 <br> - 파일을 오픈하려는 시도가 있기 전에 character_set_client에서 character_set_filesystem으로 변환 <br> - 기본값은 binary인데, 이것은 아무런 변환이 없다는 것을 의미 <br> - 멀티 바이트 파일 이름을 사용할 수 있는 시스템에서는 서로 다른 값을 사용하도록 함 <br> - 예를 들면 시스템이 UTF8로 파일이름을 표시한다면, 'utf8'로 설정 <br> - 이 변수는 MySQL 5.1.6에서 추가 <br> - 각 커넥션에서 임의의 문자집합으로 변경해서 사용 가능 | +| **character_set_results** | - MySQL Server가 쿼리의 처리 결과 또는 error 를 Client로 retuen 할 때 사용하는 Character set <br> - 각 커넥션에서의 임의의 문자집합으로 변경해서 사용 가능 | +| **character_set_server** | - MySQL Server의 Default Character set <br> - DB나 TABLE 또는 COLUMN에 아무런 문자집합이 설정되지 않았을 때, 이 변수의 값이 기본값으로 사용됨 <br> - 각 커넥션에서의 임의의 문자집합으로 변경해서 사용 가능 | +| **character_set_system** | - MySQL Server가 T 식별자(Identifier, 테이블명이나 컬럼명 등)를 저장하기 위해 사용하는 캐릭터셋 <br> - 항상 utf8로 설정되어있으며, 사용자가 설정하거나 변경할 수 없음 | + + +### 2. Time Zone 설정 + +- time_zone = **Asia/Seoul** + +### 3. Collation 설정 + +- collation_connection, collation_server = utf8_unicode_ci + +파라미터 그룹 설정 완료 후 아까 만들어둔 RDS 인스턴스에 접속하여 수정 진행. + +파라미터 그룹을 방금 만든것으로 수정 후 즉시적용 선택 + + + +RDS 인스턴스 수정이 완료되면 상태가 되면, 재부팅을 해주어야 적용이 된다. \ No newline at end of file diff --git "a/_posts/2024-05-22-aws09_S3\354\227\220 \353\214\200\355\225\234 \352\270\260\353\263\270 \352\260\234\353\205\220.md" "b/_posts/2024-05-22-aws09_S3\354\227\220 \353\214\200\355\225\234 \352\270\260\353\263\270 \352\260\234\353\205\220.md" new file mode 100644 index 000000000000..86bc52beb297 --- /dev/null +++ "b/_posts/2024-05-22-aws09_S3\354\227\220 \353\214\200\355\225\234 \352\270\260\353\263\270 \352\260\234\353\205\220.md" @@ -0,0 +1,149 @@ +--- +layout: single +title: "S3에 대한 기본 개념" +categories: [AWS] +tag: [AWS, S3] +toc: true +toc_sticky: true +post-header: false + +--- + +## S3 (Simple Storage Service)의 특징 + +안전하고 가변적인 Object 저장공간을 제공한다. (ex. Google Cloud) + +- AWS에서 모든 파일에 일종의 안전장치를 걸어 놓아서 외부에서 접근은 불가능하게 만들어준다. +- 우리가 따로 저장 공간의 크기를 직접 만지지 않아도 자기가 알아서 크기를 조절한다. + +편리한 UI 인터페이스를 통해 어디서나 쉽게 데이터를 저장하고 불러올 수 있다. + +파일 크기는 파일당 0KB부터 최대 5TB까지 지원한다. + +저장공간이 무제한이다. (디스크 크기를 따로 할당할 필요가 없다) + +Bucket 이라는 이름을 사용한다. (디렉토리와 유사함) + +Bucket은 보편적인 namespace를 사용한다. (고유한 성질) + +- IAM과 마찬가지로 S3는 리전이 글로벌이기 때문에 리전과 상관없이 Bucket 이름은 고유해야한다. + +## S3 Object 구성요소 + +- Key (파일명) +- Value (파일에 대한 데이터) +- Version ID (S3 고유 특징) + - 똑같은 파일인데 다른 버전으로 올릴 수 있게 해줄수 있다. +- Metadata (언제 업로드 되었는지, 오너는 누구인지 등등) +- CORS (한 bucket의 파일을 다른 bucket에서 접근 가능하게 해주는 기능) + +## S3 Data Consistency Model (S3 일관성 모델) + +1. Read after Write Consistency (PUT) - 데이터 일관성 + - PUT 동작(쓰기 후 읽기) 에 대해서는 데이터 일관성을 보장해준다. + - 파일이 S3 Bucket에 올라간다면 그 파일을 즉시 사용할 수 있다는 뜻이다. +2. Eventual Consistency (UPDATE, DELETE) - 최종 일관성 + - put과 달리 내가 파일을 update/delete 했을 때 바로 누가 읽게되면 그 파일이 update/delete가 안되어 있는 상태일 수 있다. + - 시간이 지남에 따라 당장은 아니지만 최종적으로 일관성이 유지된다는 뜻이다. + +## S3 스토리지 종류 + +- 일반 S3 +- S3 - IA(Infrequent Access) +- S3 - One Zone IA +- Glacier +- Intelligent Tiering + +### 1. 일반 S3 + +가장 보편적으로 사용되는 스토리지 타입이다. + +높은 내구성 (데이터 손실 없이), 높은 가용성 (데이터 접근 용이) + +### 2. S3 - IA (Infrequent Access) + +자주 접근되지는 않으나 접근시 빠른 접근이 요구되는 파일이 많을시 유용하다. + +일반 S3에 비해 비용은 저렴하나 접근시 추가 비용이 발생한다. → 접근이 잦은 데이터를 보관하기에는 적합X + +Multi-AZ로 인해 가용성이 상당히 높다. + +### 3. S3 - One Zone IA + +단일 AZ를 통한 데이터 저장 → 단일 AZ로 인해 데이터 접근 제한 (조금 낮은 가용성) + +데이터 접근시 S3 - IA 보다 20% 비용 저렴하다. + +### 4. Glacier + +거의 접근하지 않을 데이터 저장 시 유용 + +매우 저렴한 비용 + +데이터 접근시 대략 4~5시간 소요된다. + +### 5. Intelligent Tiering + +데이터 접근 주기를 알아서 분석한 후 두가지 티어 중 하나로 분류해준다. + +- Frequent Tier (비용이 약간 더 비쌈) +- Infrequent Tier + +데이터 접근 주기가 불규칙할 때 매우 유용하다. + +최고의 비용 절감 효율을 누릴 수 있다. + +## S3 사용 용례 + +- 파일 저장소 (로그, 다양한 파일들-이미지, 비디오, 압축파일 등) +- 웹사이트 호스팅 - HTML, CSS, Javascript 등의 파일을 올려서 실제로 웹 사이트를 돌리기 위한 파일들을 업로드한 후 S3 Bucket을 실제 도메인 DNS로 사용할 수 있다. +- CORS + +### CORS (Cross-Origin Resource Sharing) + +현재 사용자가 접속한 웹 애플리케이션이 다른 출처의 리소스를 불러올 때, + +Access-Control-Allow-Origin 헤더를 보내주지 않으면 브라우저가 그 리소스를 거부하는 정책으로, + +웹 브라우저에서 보안상의 이유로 도입되었음. + +S3에서는 접근 제어 리스트(ACL), CORS 정책(CORS Policy)를 설정하여 액세스 권한을 부여할 수 있다. + +(IAM으로 사용자의 버킷 권한 액세스도 관리 가능) + +## S3 암호화 + +- 파일 업로드/다운로드 시 + - S3에서 파일을 업로드,다운로드 시 SSL/TLS 동작 +- 가만히 있을 시 + 1. SEE-S3 + 2. SSE-KMS + 3. SSE-C + +SEE-S3 + +- 기본적으로 S3 Bucket에 저장되어 있는 모든 Object들은 고유한 키를 가지고 있다. +- SEE-S3는 마스터키라는 것을 가지고 일정 시간마다 고유 키값들을 변경시킨다. (우리가 API를 접근할 때 부여받는 토큰 처럼) + +SEE-KMS + +- KMS 언제 어떻게 암호를 풀었는지에 대한 기록이 복원되어 있어 좀 더 체계적인 관리를 가능하게 해준다. + +SEE-C + +- 우리가 암호키를 직접 다룰 수 있으며 따라서 키값을 우리가 변경시켜줘야 한다. + +### S3 암호화 과정 + +- PUT 요청이 생성되었을 때 헤더 + +  + + +PUT 요청 헤더에 ‘x-amz-server-side-encryption-parameter’가 들어있으면 S3는 암호화 요청을 건 것으로 간주한다. + +위의 헤더 내용처럼 simon-image.jpg라는 파일이 업로드 될 시에 S3는 AES-256 암호화 알고리즘을 사용하여 암호화할 것이다. + +암호화가 걸리지 않은 파일은 Bucket에 못올리게 하는 기능은 없을까? + +→ S3의 버킷 정책에서 설정이 가능함. \ No newline at end of file diff --git "a/_posts/2024-06-02-aws10_S3 \354\203\235\354\204\261 & \355\215\274\353\270\224\353\246\255 ACL \354\204\244\354\240\225\355\225\230\354\227\254 \354\231\270\353\266\200 \354\240\221\352\267\274\355\225\264\353\263\264\352\270\260.md" "b/_posts/2024-06-02-aws10_S3 \354\203\235\354\204\261 & \355\215\274\353\270\224\353\246\255 ACL \354\204\244\354\240\225\355\225\230\354\227\254 \354\231\270\353\266\200 \354\240\221\352\267\274\355\225\264\353\263\264\352\270\260.md" new file mode 100644 index 000000000000..58571c7c5149 --- /dev/null +++ "b/_posts/2024-06-02-aws10_S3 \354\203\235\354\204\261 & \355\215\274\353\270\224\353\246\255 ACL \354\204\244\354\240\225\355\225\230\354\227\254 \354\231\270\353\266\200 \354\240\221\352\267\274\355\225\264\353\263\264\352\270\260.md" @@ -0,0 +1,110 @@ +--- +layout: single +title: "S3 생성 & 퍼블릭 ACL 설정하여 외부 접근해보기" +categories: [AWS] +tag: [AWS, S3] +toc: true +toc_sticky: true +post-header: false + +--- + +## S3 생성 + +버킷 이름은 리전과 상관없이 중복되는 버킷 이름은 줄 수 없다. + +그리고, ‘버킷 선택’ 버튼을 통해 기존에 생성된 bucket 설정을 그대로 가져와서 지금 만들 bucket에 그대로 적용시키는 기능이 있다. + + + +ACL을 비활성하면, 루트 유저를 제외한 모든 사람은 이 bucket 객체 소유권을 가질 수 없다. + +따라서, 객체 소유권은 bucket 오너만 적용된다는 뜻이다. + +테스트용 정적 페이지 제공을 위해서 ACL을 활성화시켰다. + + + +퍼블릭 엑세스 차단 설정으로 외부로부터 접근이 불가능하도록 할 수 있다. 처음 bucket을 만들 때 이렇게 퍼블릭 접근을 차단하는 기능이 디폴트로 설정되어 있다. 그리고 AWS에서도 외부 접근을 차단하는 것을 매우 권장하고 있는 부분이다. + +외부에서 S3 버킷으로 액세스가 되는 것을 테스트해보기 위해 버킷의 퍼블릭 엑세스를 차단을 해지했다. 하지만 사용목적에 따라 보안상 알맞게 설정해서 사용해야한다. + + + +버전 관리를 활성화하면 같은 bucket에서 똑같은 파일을 업로드하더라도 파일이 덮어씌워지는 것이 아니라 다른 버전으로 관리가 되어진다. 파일명은 똑같아도 콘텐츠는 달라질 수 있는데 여기서 다른 버전을 선택해서 우리가 원하는 객체를 사용할 수 있다. + + + +기본 암호화는 객체를 bucket에 업로드할 때 자동으로 암호화를 걸 수 있는 설정이다. 암호화 키 유형은 SSE-S3, SSE-KMS, DSSE-KMS 세가지 유형이 제공된다. + + + +마지막으로 고급설정을 열어보면 객체 잠금에 대한 항목이 나온다. 만약 객체 잠금을 활성화 시킨다면 bucket에 파일을 한 번 업로드한 이상 파일을 덮어씌우거나 삭제가 불가능해진다. 이는 bucket에 원본 파일을 오랜 시간동안 복원하고 싶을 경우 사용되는 것이다. + + + +bucket 생성 완료! + +## 파일 업로드 + +이제 만들어진 버킷에 외부에서 접근할 오브젝트를 생성해보자. + +방금 만든 bucket으로 들어와 폴더를 생성해준다. + + + +images라는 폴더를 하나 생성해본다. + +AWS는 이렇게 폴더에도 암호화를 있게 제공해준다. bucket은 암호화가 걸려있지 않더라도 이렇게 특정 폴더만 암호화를 걸 수 있게 세분화되어 있다. + + + +파일 업로드 하기. + +images 폴더로 들어와서 업로드를 클릭하고, 간단한 이미지 하나를 업로드 해본다. + + + +권한 설정을 통해 파일에 대한 소유권을 정의할 수 있다. 버킷을 생성할 때 ACL을 비활성화하면 ‘이 bucket에는 객체 소유권에 대한 버킷 소유자 적용 설정이 적용되어 있습니다.’ 라는 문구가 뜬다. + + + +다음은 스토리지 클래스를 지정할 수 있는데, 객체의 용도에 따라 설정해주면 된다. + + + +여기서도 서버측 암호화를 설정할 수 있는데, 지정하지 않음으로 설정했다. + + + +체크섬 설정은 데이터 무결성 확인을 위해 사용되는 것이다. S3 bucket 오브젝트는 고유한 MD5 체크섬을 가지고 있는데 여기에 이 태그를 포함하여 데이터 무결성을 확인할 때 사용되어진다. + + + +마지막으로 태그와 메타데이터를 정의할 수 있는데, 여기 태그를 만듦으로써 bucket에 들어있는 오브젝트들을 태그별로 묶어서 따로 관리할 수 있다. + + + +만들어진 폴더에 이미지 파일 하나를 업로드한 후에 파일 정보에 대해 확인해본다. + +오른쪽 상단에 ‘열기’ 버튼으로도 확인할 수 있지만 우린 외부에서 접근 테스트를 하기 위해 객체 URL을 통해 접속해봐야 한다. + + + +객체 URL로 접근을 하면 다음과 같이 Access denied가 뜨게 된다. + + + +그럼 이제 해당 객체의 권한을 확인해보자. + +권한을 확인해보면 객체 소유자에 대해서 객체: 읽기, 객체 ACL: 읽기,쓰기 로 설정되어 있다. + + + +우린 외부에서 접근 테스트를 하기 위해 ‘편집’을 통해 퍼블릭 엑세스에 대하여 객체:읽기 설정을 해주자. + + + +해당 내용으로 설정한 후에 다시 객체 URL로 접속하면 이제 우리가 업로드한 객체가 외부에서 접근이 가능하게 설정이 되고, URL을 통해 파일을 확인할 수 있다. + + \ No newline at end of file diff --git "a/_posts/2024-06-04-aws11_S3 \353\262\204\355\202\267 \354\240\225\354\261\205 \354\204\244\354\240\225 & \354\225\224\355\230\270\355\231\224\355\225\230\352\270\260.md" "b/_posts/2024-06-04-aws11_S3 \353\262\204\355\202\267 \354\240\225\354\261\205 \354\204\244\354\240\225 & \354\225\224\355\230\270\355\231\224\355\225\230\352\270\260.md" new file mode 100644 index 000000000000..57f20558e661 --- /dev/null +++ "b/_posts/2024-06-04-aws11_S3 \353\262\204\355\202\267 \354\240\225\354\261\205 \354\204\244\354\240\225 & \354\225\224\355\230\270\355\231\224\355\225\230\352\270\260.md" @@ -0,0 +1,212 @@ +--- +layout: single +title: "S3 버킷 정책 설정 & 암호화하기" +categories: [AWS] +tag: [AWS, S3] +toc: true +toc_sticky: true +post-header: false + +--- + +## S3 버킷 정책 설정하기 + +버킷 정책을 JSON 형식으로 설정해볼건데, 정책 생성기를 사용하여 특정 AWS 사용자 + +생성할 버킷 정책 : 내가 만든 버킷(myk-s3-bucket)에 Object를 put/get 할 수 있고, 버킷의 위치와 버킷 리스트를 가져 올수 있는 정책을 설정할 것이다. 단, 내 IP에서만 이 행위들을 할 수 있게 설정해보자. + +버킷 > 권한 > 버킷 정책으로 이동 + + + +편집으로 들어가서 정책생성기를 이용하면 편리하게 정책을 JOSN형태로 생성할 수 있다. (아쉽게도 한글 지원은 하지 않는다.) + + + +- Select Type of Policy : S3 Bucket Policy (S3 버킷 정책) +- Effect : Allow (허용할 권한을 생성할 것이기 때문에) +- Principal : AWS 유저의 ARN (정책을 적용할 AWS 유저를 지정한다. → IAM에서 해당 유저의 ARN을 복사하면 된다, 여러 유저라면 ‘,’로 지정하면 되고 전체 IAM 유저를 지정하려면 ‘*’를 입력) + +  + +- Actions : get Obejct, put Object (정책에 해당하는 action을 지정) +- Amazon Resource Name : arn:aws:s3:::myk-s3-bucket/* (정책을 적용할 리소스 지정) + - 버킷 정책 편집에 내 버킷 ARN을 복사한 후 버킷 아래의 모든 Object들에 적용할 거기 때문에 /* 붙여줌 + +  + + - 특정 폴더별 혹은 객체로 지정하려면 /folder_name/* 혹은 /folder_name/object_name으로 지정 + + + +접근 허용할 IP를 지정하려면 Add Conditions를 선택하여 해당 옵션을 주면 된다. + +- Condition : IpAddress +- Key : aws:SourceIp +- Value : 허용할 IP 입력 (나의 IP로 입력했다.) + + + +입력한 후 Add Condition 클릭 후 Add Statement를 클릭하면 방금 생성한 정책이 정책 생성기 리스트에 추가된다. + + + +이제 GetBucketLocation, ListBucket을 추가한다. + +따로 하는 이유는 GetObejct, PutObject는 적용할 리소스가 객체 단위이므로 “Resouerce”값에 arn:aws:s3:::버킷명/* 으로 지정해주었다. + +하지만, GetBucketLocation, ListBucket는 적용할 리소스가 버킷 단위이므로 “Resouerce”값에 /*가 들어간다면 생성시 에러가 난다. + +그래서 나는 먼저 GetObject, PutObject를 추가 후에 생성하였다. + +- Actions : GetBucketLocation, ListBucket +- Amazon Resource Name : arn:aws:s3:::myk-s3-bucket + +  + + +Generate Policy를 누르면 우리가 생성한 버킷 정책을 JSON형태로 한번에 보여준다. + +이걸 복사해서 버킷 정책 편집 화면에 붙여넣자. + +생성된 버킷 정책 JSON + +```json +{ + "Version": "2012-10-17", + "Id": "Policy1724825386935", // 정책 ID + "Statement": [ + { + "Sid": "Stmt1724825131984", + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam::746669191631:user/aws-learner" + }, + "Action": [ // 버킷에 수행할 액션(행동) + "s3:PutObject", // 객체 업로오기 + "s3:GetObject" // 객체 가져오기 + ], + "Resource": "arn:aws:s3:::myk-s3-bucket/*", // 대상이 되는 리소스 + "Condition": { // 조건 + "IpAddress": { // 아래 IP들을 허용 + "aws:SourceIp": "218.157.44.117" + } + } + }, + { + "Sid": "Stmt1724825386050", + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam::746669191631:user/aws-learner" + }, + "Action": [ + "s3:GetBucketLocation", // 버켓 위치 가져오기 + "s3:ListBucket" // 버켓 리스트 확인 + ], + "Resource": "arn:aws:s3:::myk-s3-bucket", // 대상이 되는 리소스 + "Condition": { + "IpAddress": { + "aws:SourceIp": "218.157.44.117" + } + } + } + ] +} +``` + +AWS CLI로 S3의 버킷 목록 조회해보기 + + + +S3에 파일 업로드 + + + +AWS CLI로 업로드되었는지 AWS 콘솔에서 확인하기 + + + +## 암호화 정책 설정 + +버킷에 객체를 업로드(Put Object)할 때 자동으로 암호화를 할 수 있는 버킷과 암호화가 안된 객체를 업로드 할 시에 호출을 거부하는 버킷을 생성해보자. + +나는 암호화용 버킷을 새로 만들었다. + +- 기본 암호화 : 내장 암호화 방식, 버킷 안에서만 암호화가 유효하다 +- 버킷 정책 : 버킷에 대한 액세스 및 권한을 제어하는데 사용한다. + - 정책을 설정하면 지정된 암호화 헤더가 없는 객체를 버킷에 넣는 API 호출을 거부한다. + - 버킷 정책은 기본 암호화 전에 평가된다. + +따라서, 버킷의 기본 암호화를 KMS로 지정하고, 버킷 정책으로 암호화 안된 객체는 거부하는 정책을 생성할 것이다. + +### 버킷 생성 + +버킷을 생성할 때, 암호화는 KMS 유형으로 지정했고, KMS 키는 AWS에서 기본으로 제공하는 키를 사용했다. + +- KMS 키의 내용을 확인하고 싶을때는 AWS > KMS > AWS 관리형 키 > aws/s3에서 확인해보면 된다고 한다. + + + +### 버킷 정책 설정 + +정책 생성기에서 버킷 정책을 생성했다. + +- Select Type of Policy : S3 Bucket Policy +- Effect : Deny (암호화 헤더가 호출은 거부할것이므로) +- Principal : * (모든 사용자에게 적용) +- Actions : Put Obejct +- ARN : 버킷_ARN/* + + + +2가지 Condition을 추가해준다. + +- kms 암호화 방식이 아니면 deny하는 조건 + + + +- header에 위와 같은 키값이 없다면 (Null 이라면) true로 발생시켜 요청을 deny하는 조건 + + + +위 두가지 설정을 끝내고 정책을 생성해준다. + +```json +{ + "Id": "Policy1724831428004", + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "Stmt1724831425204", + "Action": [ + "s3:PutObject" + ], + "Effect": "Deny", + "Resource": "arn:aws:s3:::myk-encryption-bucket/*", + "Condition": { + "StringNotEquals": { + "s3:x-amz-server-side-encryption": "aws:kms" + }, + "Null": { + "s3:x-amz-server-side-encryption": "true" + } + }, + "Principal": "*" + } + ] +} +``` + +이제 버킷의 기본 암호화와 버킷 정책 설정이 끝났으니 테스트를 해본다. + +이미지 객체를 AWS 콘솔에서 암호화 없이 직접 업로드 한 결과 업로드 실패가 뜬것을 확인할 수 있다. + + + +AWS CLI로 별도 암호화 설정 없이 업로드를 해보면 역시 deny된 것을 확인 할 수 있다. + + + +반대로 kms 암호화하여 업로드를 하면 성공할 수 있다. + + diff --git "a/_posts/2024-06-05-aws12_S3 CORS \354\240\225\354\261\205 \354\213\244\354\212\265\355\225\264\353\263\264\352\270\260.md" "b/_posts/2024-06-05-aws12_S3 CORS \354\240\225\354\261\205 \354\213\244\354\212\265\355\225\264\353\263\264\352\270\260.md" new file mode 100644 index 000000000000..87dc86ff4253 --- /dev/null +++ "b/_posts/2024-06-05-aws12_S3 CORS \354\240\225\354\261\205 \354\213\244\354\212\265\355\225\264\353\263\264\352\270\260.md" @@ -0,0 +1,185 @@ +--- +layout: single +title: "S3 CORS 테스트해보기" +categories: [AWS] +tag: [AWS, S3] +toc: true +toc_sticky: true +post-header: false + +--- + +S3에서 CORS를 적용하여 다른 Origin에서 접근을 하고자 한다면 Headers에 Cors정책을 추가해 주어야 한다. + +외부에서 객체를 액세스할 버킷을 하나 생성해준다. + +이 버킷을 생성할 땐 퍼블릭 엑세스 차단을 비활성화 해주고 생성하고, 3개의 파일을 업로드해준다. + +그리고 정적 호스팅을 활성화해준다. + + + +해당 내용으로 버킷 정책을 설정해준다 + +위 버킷을 생성할때 처럼 단순히 퍼블릭 액세스를 모두 해제해주면 누구나 해당 데이터에 접근이 가능하기 때문에 퍼블릭 액세스를 허용한 다음에는 버킷 정책을 반드시 설정해주어야 한다. + +버킷 정책을 설정 안하면 정적 호스팅한 url로 접속시 403 Forbidden 에러 뜸. + +```json +{ + "Version": "2012-10-17", + "Id": "Policy1724833189112", + "Statement": [ + { + "Sid": "Stmt1724833188297", + "Effect": "Allow", + "Principal": "*", + "Action": "s3:GetObject", + "Resource": "arn:aws:s3:::myk-cors-bucket/*" + } + ] +} +``` + +index.html 내용 + +```html +<html> +<head> + <title>AWS TEST</title> +</head> +<body> + <h1>AWS S3 Testing..</h1> + <p>Hello</p> +</body> + +<img src="cloud.png" width="500" /> +</html> +``` + +error.html 내용 + +```html +<h1>Error</h1> +``` + +이제 이 버킷에 액세스를 제공해줄 버킷을 생성해준다. 이때 만드는 버킷은 퍼블릭 액세스를 비활성화 한 상태로 만들어야 하며, 기존에 만든 버킷과는 다른 리전을 가지고 있어야 한다. + +다른 리전을 가지게 됨으로써 이 버킷은 이전에 만든 버킷과 다른 Origin으로 형성하는 것임. + +이 버킷은 리전 도쿄(ap-northeast-1)에 만들었고, 4개의 파일을 업로드해준다. + + + +index.html 내용 + +```html +<html> +<head> + <title>AWS TEST</title> +</head> +<body> + <h1>AWS S3 Testing..</h1> + <p>Hello</p> +</body> + +<img src="cloud.png" width="500" /> + +<!-- CORS demo --> + +<div id="tofetch"/> +<script> + let tofetch = document.getElementById("tofetch") + + fetch('extra.html').then((response) =>{ + console.log("response",response) + return response.text(); + }).then((html) =>{ + console.log("html",html) + tofetch.innerHTML = html + }) + +</script> +</html> +``` + +extra.html 내용 + +```html +<h1>extra Page</h1> +``` + +이제 새롭게 만든 버킷의 웹 호스팅을 활성화해주고, 버킷 정책을 동일하게 설정해준다. + +여기서, 버킷 정책을 등록할 때 먼저 버킷의 퍼블릭 액세스 차단을 비활성화 해준다. + +퍼블릭 액세스 차단 상태에서는 버킷 정책을 등록,수정을 할 수 없기 때문에 일단 비활성화 한 후, 버킷 정책을 수정,등록한다. + +처음 만들었던 버킷(myk-cors-bucket)의 index.html 파일을 수정해서 확인해보자. + +도쿄 리전에 만들었던 버킷의 extra.html 리소스를 이 버킷의 index.html에서 불러오도록 수정해본다. + +```html +<html> +<head> + <title>AWS TEST</title> +</head> +<body> + <h1>AWS S3 Testing..</h1> + <p>Hello</p> +</body> + +<img src="cloud.png" width="500" /> + +<!-- CORS demo --> + +<div id="tofetch"/> +<script> + let tofetch = document.getElementById("tofetch") + fetch('http://myk-cors-test-bucket.s3-website-ap-northeast-1.amazonaws.com/extra.html').then((response) =>{ + console.log("response",response) + return response.text(); + }).then((html) =>{ + console.log("html",html) + tofetch.innerHTML = html + }) + +</script> +</html> +``` + + + +index.html의 내용을 위와 같이 수정한 후 웹 url에 접속하면 extra.html 파일이 불러와지지 않고 콘솔에 해당 에러가 찍히게 된다. (Access-Control-Allow-Origin) + + + +이런 경우에 S3 CORS 설정을 통해 해결해보자. + +액세스를 시도할 버킷의 권한으로 이동 → 가장 하단에 있는 CORS 편집 + +→ 맨 아래 CORS 편집에 해당 json을 넣는다. + +- AllowedOrigins 에는 요청을 보내는 버킷의 URL + +```json +[ + { + "AllowedHeaders": [ + "Authorization" + ], + "AllowedMethods": [ + "GET" + ], + "AllowedOrigins": [ + "http://myk-cors-bucket.s3-website.ap-northeast-2.amazonaws.com" + ], + "ExposeHeaders": [], + "MaxAgeSeconds": 3000 + } +] +``` + +해당 설정 후 다시 url로 접속하면 이번엔 해당 리소스가 가져와지는 것을 확인할 수 있다. + + \ No newline at end of file diff --git a/_sass/minimal-mistakes/_reset.scss b/_sass/minimal-mistakes/_reset.scss index 6380590318c5..6f0147047922 100644 --- a/_sass/minimal-mistakes/_reset.scss +++ b/_sass/minimal-mistakes/_reset.scss @@ -8,18 +8,18 @@ html { /* apply a natural box layout model to all elements */ box-sizing: border-box; background-color: $background-color; - font-size: 16px; + font-size: 14px; @include breakpoint($medium) { - font-size: 18px; + font-size: 16px; } @include breakpoint($large) { - font-size: 20px; + font-size: 16px; } @include breakpoint($x-large) { - font-size: 22px; + font-size: 16px; } -webkit-text-size-adjust: 100%; diff --git a/assets/css/main.scss b/assets/css/main.scss index 1f0ead318516..969163751a70 100644 --- a/assets/css/main.scss +++ b/assets/css/main.scss @@ -7,3 +7,10 @@ search: false @import "minimal-mistakes/skins/{{ site.minimal_mistakes_skin | default: 'default' }}"; // skin @import "minimal-mistakes"; // main partials + +@import url('https://fonts.googleapis.com/css?family=Inconsolata'); + +.archive a { + color: #696969; // 메인 포스트 a태그 제목 색 + text-decoration: none; +} diff --git a/assets/images/DL01/img1.png b/assets/images/DL01/img1.png new file mode 100644 index 000000000000..0e153986b85a Binary files /dev/null and b/assets/images/DL01/img1.png differ diff --git a/assets/images/DL01/img10.png b/assets/images/DL01/img10.png new file mode 100644 index 000000000000..90b91a1906cb Binary files /dev/null and b/assets/images/DL01/img10.png differ diff --git a/assets/images/DL01/img2.png b/assets/images/DL01/img2.png new file mode 100644 index 000000000000..f1408e5bbb0e Binary files /dev/null and b/assets/images/DL01/img2.png differ diff --git a/assets/images/DL01/img3.png b/assets/images/DL01/img3.png new file mode 100644 index 000000000000..d41985d31c26 Binary files /dev/null and b/assets/images/DL01/img3.png differ diff --git a/assets/images/DL01/img4.png b/assets/images/DL01/img4.png new file mode 100644 index 000000000000..1da3d50907a0 Binary files /dev/null and b/assets/images/DL01/img4.png differ diff --git a/assets/images/DL01/img5.png b/assets/images/DL01/img5.png new file mode 100644 index 000000000000..67e776e29b4d Binary files /dev/null and b/assets/images/DL01/img5.png differ diff --git a/assets/images/DL01/img6.png b/assets/images/DL01/img6.png new file mode 100644 index 000000000000..0bb59b24ce4f Binary files /dev/null and b/assets/images/DL01/img6.png differ diff --git a/assets/images/DL01/img9.png b/assets/images/DL01/img9.png new file mode 100644 index 000000000000..c8e227108f57 Binary files /dev/null and b/assets/images/DL01/img9.png differ diff --git a/assets/images/DL02/01_1.png b/assets/images/DL02/01_1.png new file mode 100644 index 000000000000..8d8bd74d2d44 Binary files /dev/null and b/assets/images/DL02/01_1.png differ diff --git a/assets/images/DL02/01_2.png b/assets/images/DL02/01_2.png new file mode 100644 index 000000000000..20587b24b6ba Binary files /dev/null and b/assets/images/DL02/01_2.png differ diff --git a/assets/images/DL02/01_3.png b/assets/images/DL02/01_3.png new file mode 100644 index 000000000000..d60cc061e47b Binary files /dev/null and b/assets/images/DL02/01_3.png differ diff --git a/assets/images/DL02/01_4.png b/assets/images/DL02/01_4.png new file mode 100644 index 000000000000..0bcc59971120 Binary files /dev/null and b/assets/images/DL02/01_4.png differ diff --git a/assets/images/DL02/01_5.png b/assets/images/DL02/01_5.png new file mode 100644 index 000000000000..7266182da7c4 Binary files /dev/null and b/assets/images/DL02/01_5.png differ diff --git a/assets/images/DL02/01_6.png b/assets/images/DL02/01_6.png new file mode 100644 index 000000000000..bba4c5815fa4 Binary files /dev/null and b/assets/images/DL02/01_6.png differ diff --git a/assets/images/DL03/02_1.png b/assets/images/DL03/02_1.png new file mode 100644 index 000000000000..cf5f18351691 Binary files /dev/null and b/assets/images/DL03/02_1.png differ diff --git a/assets/images/DL03/02_2.png b/assets/images/DL03/02_2.png new file mode 100644 index 000000000000..b0fd03e1df86 Binary files /dev/null and b/assets/images/DL03/02_2.png differ diff --git a/assets/images/DL04/output_18_0.png b/assets/images/DL04/output_18_0.png new file mode 100644 index 000000000000..fa793d294c1b Binary files /dev/null and b/assets/images/DL04/output_18_0.png differ diff --git a/assets/images/DL05/05_1.png b/assets/images/DL05/05_1.png new file mode 100644 index 000000000000..099bcab97138 Binary files /dev/null and b/assets/images/DL05/05_1.png differ diff --git a/assets/images/DL05/05_2.png b/assets/images/DL05/05_2.png new file mode 100644 index 000000000000..9cf687c7ad25 Binary files /dev/null and b/assets/images/DL05/05_2.png differ diff --git a/assets/images/DL05/05_3.png b/assets/images/DL05/05_3.png new file mode 100644 index 000000000000..279fe352763a Binary files /dev/null and b/assets/images/DL05/05_3.png differ diff --git a/assets/images/DL05/05_4.png b/assets/images/DL05/05_4.png new file mode 100644 index 000000000000..fe8458176494 Binary files /dev/null and b/assets/images/DL05/05_4.png differ diff --git a/assets/images/DL05/05_5.png b/assets/images/DL05/05_5.png new file mode 100644 index 000000000000..3ff90cbd12c9 Binary files /dev/null and b/assets/images/DL05/05_5.png differ diff --git a/assets/images/DL06/09_3.png b/assets/images/DL06/09_3.png new file mode 100644 index 000000000000..65712511017d Binary files /dev/null and b/assets/images/DL06/09_3.png differ diff --git a/assets/images/DL06/09_4.jpg b/assets/images/DL06/09_4.jpg new file mode 100644 index 000000000000..0156801efe43 Binary files /dev/null and b/assets/images/DL06/09_4.jpg differ diff --git a/assets/images/DL06/09_5.png b/assets/images/DL06/09_5.png new file mode 100644 index 000000000000..767e6622e00e Binary files /dev/null and b/assets/images/DL06/09_5.png differ diff --git a/assets/images/DL06/result1.png b/assets/images/DL06/result1.png new file mode 100644 index 000000000000..7739c4188cb7 Binary files /dev/null and b/assets/images/DL06/result1.png differ diff --git a/assets/images/DL06/result2.png b/assets/images/DL06/result2.png new file mode 100644 index 000000000000..7ba83434b4e3 Binary files /dev/null and b/assets/images/DL06/result2.png differ diff --git a/assets/images/DL06/result3.png b/assets/images/DL06/result3.png new file mode 100644 index 000000000000..a3586100ce9f Binary files /dev/null and b/assets/images/DL06/result3.png differ diff --git a/assets/images/DL07/07_1.png b/assets/images/DL07/07_1.png new file mode 100644 index 000000000000..a1ac68c58b92 Binary files /dev/null and b/assets/images/DL07/07_1.png differ diff --git a/assets/images/DL07/07_10.png b/assets/images/DL07/07_10.png new file mode 100644 index 000000000000..6a54ce6331ae Binary files /dev/null and b/assets/images/DL07/07_10.png differ diff --git a/assets/images/DL07/07_11.png b/assets/images/DL07/07_11.png new file mode 100644 index 000000000000..27e2a8115178 Binary files /dev/null and b/assets/images/DL07/07_11.png differ diff --git a/assets/images/DL07/07_12.png b/assets/images/DL07/07_12.png new file mode 100644 index 000000000000..8988b93f138f Binary files /dev/null and b/assets/images/DL07/07_12.png differ diff --git a/assets/images/DL07/07_2.png b/assets/images/DL07/07_2.png new file mode 100644 index 000000000000..b4add6ed0c9a Binary files /dev/null and b/assets/images/DL07/07_2.png differ diff --git a/assets/images/DL07/07_3_1.png b/assets/images/DL07/07_3_1.png new file mode 100644 index 000000000000..2554a51aa369 Binary files /dev/null and b/assets/images/DL07/07_3_1.png differ diff --git a/assets/images/DL07/07_3_2.png b/assets/images/DL07/07_3_2.png new file mode 100644 index 000000000000..5722c1545f0e Binary files /dev/null and b/assets/images/DL07/07_3_2.png differ diff --git a/assets/images/DL07/07_4.png b/assets/images/DL07/07_4.png new file mode 100644 index 000000000000..7498d21b9b69 Binary files /dev/null and b/assets/images/DL07/07_4.png differ diff --git a/assets/images/DL07/07_5.png b/assets/images/DL07/07_5.png new file mode 100644 index 000000000000..eea3893c6880 Binary files /dev/null and b/assets/images/DL07/07_5.png differ diff --git a/assets/images/DL07/07_6.png b/assets/images/DL07/07_6.png new file mode 100644 index 000000000000..828ffd936d43 Binary files /dev/null and b/assets/images/DL07/07_6.png differ diff --git a/assets/images/DL07/07_7.png b/assets/images/DL07/07_7.png new file mode 100644 index 000000000000..8758383debaf Binary files /dev/null and b/assets/images/DL07/07_7.png differ diff --git a/assets/images/DL07/07_8.png b/assets/images/DL07/07_8.png new file mode 100644 index 000000000000..5322883164f6 Binary files /dev/null and b/assets/images/DL07/07_8.png differ diff --git a/assets/images/DL07/07_9.png b/assets/images/DL07/07_9.png new file mode 100644 index 000000000000..d0f12a9fd54d Binary files /dev/null and b/assets/images/DL07/07_9.png differ diff --git a/assets/images/DL07/Untitled.png b/assets/images/DL07/Untitled.png new file mode 100644 index 000000000000..9e1b58f5dc21 Binary files /dev/null and b/assets/images/DL07/Untitled.png differ diff --git a/assets/images/DL08-1/output_19_0.png b/assets/images/DL08-1/output_19_0.png new file mode 100644 index 000000000000..b7e2522d6ec7 Binary files /dev/null and b/assets/images/DL08-1/output_19_0.png differ diff --git a/assets/images/DL08-1/output_21_0.png b/assets/images/DL08-1/output_21_0.png new file mode 100644 index 000000000000..2bf2efc14a70 Binary files /dev/null and b/assets/images/DL08-1/output_21_0.png differ diff --git a/assets/images/DL08-1/output_29_0.png b/assets/images/DL08-1/output_29_0.png new file mode 100644 index 000000000000..d17687a2a8ba Binary files /dev/null and b/assets/images/DL08-1/output_29_0.png differ diff --git a/assets/images/DL08-1/output_34_0.png b/assets/images/DL08-1/output_34_0.png new file mode 100644 index 000000000000..70b54f8eff1d Binary files /dev/null and b/assets/images/DL08-1/output_34_0.png differ diff --git a/assets/images/DL08-1/output_40_0.png b/assets/images/DL08-1/output_40_0.png new file mode 100644 index 000000000000..1f1827a14da9 Binary files /dev/null and b/assets/images/DL08-1/output_40_0.png differ diff --git "a/assets/images/GRU\354\205\200.png" "b/assets/images/GRU\354\205\200.png" new file mode 100644 index 000000000000..67604eec7eb5 Binary files /dev/null and "b/assets/images/GRU\354\205\200.png" differ diff --git a/assets/images/LSTM_cell_update.png b/assets/images/LSTM_cell_update.png new file mode 100644 index 000000000000..d838218861a1 Binary files /dev/null and b/assets/images/LSTM_cell_update.png differ diff --git "a/assets/images/LSTM_cell\354\203\201\355\203\234.png" "b/assets/images/LSTM_cell\354\203\201\355\203\234.png" new file mode 100644 index 000000000000..1e15571e388b Binary files /dev/null and "b/assets/images/LSTM_cell\354\203\201\355\203\234.png" differ diff --git "a/assets/images/LSTM_forget\352\262\214\354\235\264\355\212\270.png" "b/assets/images/LSTM_forget\352\262\214\354\235\264\355\212\270.png" new file mode 100644 index 000000000000..ebf842727866 Binary files /dev/null and "b/assets/images/LSTM_forget\352\262\214\354\235\264\355\212\270.png" differ diff --git "a/assets/images/LSTM_input\352\262\214\354\235\264\355\212\270.png" "b/assets/images/LSTM_input\352\262\214\354\235\264\355\212\270.png" new file mode 100644 index 000000000000..a23c5f6f793a Binary files /dev/null and "b/assets/images/LSTM_input\352\262\214\354\235\264\355\212\270.png" differ diff --git "a/assets/images/LSTM_output\352\262\214\354\235\264\355\212\270.png" "b/assets/images/LSTM_output\352\262\214\354\235\264\355\212\270.png" new file mode 100644 index 000000000000..c651d44b34b0 Binary files /dev/null and "b/assets/images/LSTM_output\352\262\214\354\235\264\355\212\270.png" differ diff --git "a/assets/images/LSTM\352\265\254\354\241\260.png" "b/assets/images/LSTM\352\265\254\354\241\260.png" new file mode 100644 index 000000000000..2a078543b6a7 Binary files /dev/null and "b/assets/images/LSTM\352\265\254\354\241\260.png" differ diff --git "a/assets/images/LSTM\352\270\260\355\230\270.png" "b/assets/images/LSTM\352\270\260\355\230\270.png" new file mode 100644 index 000000000000..a112c1c77bc1 Binary files /dev/null and "b/assets/images/LSTM\352\270\260\355\230\270.png" differ diff --git a/assets/images/MyProfile.png b/assets/images/MyProfile.png new file mode 100644 index 000000000000..f7b066a32ecc Binary files /dev/null and b/assets/images/MyProfile.png differ diff --git a/assets/images/RNN.png b/assets/images/RNN.png new file mode 100644 index 000000000000..b40d64761f3c Binary files /dev/null and b/assets/images/RNN.png differ diff --git "a/assets/images/RNN\354\210\230\354\213\235.PNG" "b/assets/images/RNN\354\210\230\354\213\235.PNG" new file mode 100644 index 000000000000..d1586b88dda2 Binary files /dev/null and "b/assets/images/RNN\354\210\230\354\213\235.PNG" differ diff --git "a/assets/images/RNN\355\231\234\354\232\251.PNG" "b/assets/images/RNN\355\231\234\354\232\251.PNG" new file mode 100644 index 000000000000..ac2062badb32 Binary files /dev/null and "b/assets/images/RNN\355\231\234\354\232\251.PNG" differ diff --git a/assets/images/Sigmoid.png b/assets/images/Sigmoid.png new file mode 100644 index 000000000000..a7d3be13c4b7 Binary files /dev/null and b/assets/images/Sigmoid.png differ diff --git "a/assets/images/Sigmoid_\354\204\261\354\247\210.png" "b/assets/images/Sigmoid_\354\204\261\354\247\210.png" new file mode 100644 index 000000000000..444bdd5fb35c Binary files /dev/null and "b/assets/images/Sigmoid_\354\204\261\354\247\210.png" differ diff --git a/assets/images/aws01/01.png b/assets/images/aws01/01.png new file mode 100644 index 000000000000..9f0aee5d5f9d Binary files /dev/null and b/assets/images/aws01/01.png differ diff --git a/assets/images/aws01/02.png b/assets/images/aws01/02.png new file mode 100644 index 000000000000..0cc1c3089152 Binary files /dev/null and b/assets/images/aws01/02.png differ diff --git a/assets/images/aws01/03.png b/assets/images/aws01/03.png new file mode 100644 index 000000000000..f0f12d71c8a1 Binary files /dev/null and b/assets/images/aws01/03.png differ diff --git a/assets/images/aws01/04.png b/assets/images/aws01/04.png new file mode 100644 index 000000000000..d5e6d4852a35 Binary files /dev/null and b/assets/images/aws01/04.png differ diff --git a/assets/images/aws01/05.png b/assets/images/aws01/05.png new file mode 100644 index 000000000000..c961b49623ea Binary files /dev/null and b/assets/images/aws01/05.png differ diff --git a/assets/images/aws01/06.png b/assets/images/aws01/06.png new file mode 100644 index 000000000000..7a8f353aa509 Binary files /dev/null and b/assets/images/aws01/06.png differ diff --git a/assets/images/aws01/07.png b/assets/images/aws01/07.png new file mode 100644 index 000000000000..f565c55a7647 Binary files /dev/null and b/assets/images/aws01/07.png differ diff --git a/assets/images/aws01/08.png b/assets/images/aws01/08.png new file mode 100644 index 000000000000..d5b5dd061700 Binary files /dev/null and b/assets/images/aws01/08.png differ diff --git a/assets/images/aws01/09.png b/assets/images/aws01/09.png new file mode 100644 index 000000000000..3523548a1750 Binary files /dev/null and b/assets/images/aws01/09.png differ diff --git a/assets/images/aws01/10.png b/assets/images/aws01/10.png new file mode 100644 index 000000000000..87be6e619762 Binary files /dev/null and b/assets/images/aws01/10.png differ diff --git a/assets/images/aws01/11.png b/assets/images/aws01/11.png new file mode 100644 index 000000000000..6cadf899ad93 Binary files /dev/null and b/assets/images/aws01/11.png differ diff --git a/assets/images/aws01/12.png b/assets/images/aws01/12.png new file mode 100644 index 000000000000..5901e4f38d0b Binary files /dev/null and b/assets/images/aws01/12.png differ diff --git a/assets/images/aws03/01.png b/assets/images/aws03/01.png new file mode 100644 index 000000000000..1616d0b660cd Binary files /dev/null and b/assets/images/aws03/01.png differ diff --git a/assets/images/aws04/01.png b/assets/images/aws04/01.png new file mode 100644 index 000000000000..7515804fbb2c Binary files /dev/null and b/assets/images/aws04/01.png differ diff --git a/assets/images/aws04/02.png b/assets/images/aws04/02.png new file mode 100644 index 000000000000..55d05762d595 Binary files /dev/null and b/assets/images/aws04/02.png differ diff --git a/assets/images/aws04/03.png b/assets/images/aws04/03.png new file mode 100644 index 000000000000..3bcf250140d6 Binary files /dev/null and b/assets/images/aws04/03.png differ diff --git a/assets/images/aws04/04.png b/assets/images/aws04/04.png new file mode 100644 index 000000000000..8ca8beaee775 Binary files /dev/null and b/assets/images/aws04/04.png differ diff --git a/assets/images/aws04/05.png b/assets/images/aws04/05.png new file mode 100644 index 000000000000..a18d20dbd007 Binary files /dev/null and b/assets/images/aws04/05.png differ diff --git a/assets/images/aws04/06.png b/assets/images/aws04/06.png new file mode 100644 index 000000000000..4ad7a07d5029 Binary files /dev/null and b/assets/images/aws04/06.png differ diff --git a/assets/images/aws04/07.png b/assets/images/aws04/07.png new file mode 100644 index 000000000000..277ec2097296 Binary files /dev/null and b/assets/images/aws04/07.png differ diff --git a/assets/images/aws04/08.png b/assets/images/aws04/08.png new file mode 100644 index 000000000000..b0f38ef3c4fc Binary files /dev/null and b/assets/images/aws04/08.png differ diff --git a/assets/images/aws04/09.png b/assets/images/aws04/09.png new file mode 100644 index 000000000000..12fcfbae8714 Binary files /dev/null and b/assets/images/aws04/09.png differ diff --git a/assets/images/aws04/10.png b/assets/images/aws04/10.png new file mode 100644 index 000000000000..5867e85c491e Binary files /dev/null and b/assets/images/aws04/10.png differ diff --git a/assets/images/aws04/11.png b/assets/images/aws04/11.png new file mode 100644 index 000000000000..22c5c3cab252 Binary files /dev/null and b/assets/images/aws04/11.png differ diff --git a/assets/images/aws04/12.png b/assets/images/aws04/12.png new file mode 100644 index 000000000000..760888565d58 Binary files /dev/null and b/assets/images/aws04/12.png differ diff --git a/assets/images/aws04/13.png b/assets/images/aws04/13.png new file mode 100644 index 000000000000..a2399e5ffce4 Binary files /dev/null and b/assets/images/aws04/13.png differ diff --git a/assets/images/aws04/14.png b/assets/images/aws04/14.png new file mode 100644 index 000000000000..a60965e1a420 Binary files /dev/null and b/assets/images/aws04/14.png differ diff --git a/assets/images/aws04/15.png b/assets/images/aws04/15.png new file mode 100644 index 000000000000..61325c92b5f8 Binary files /dev/null and b/assets/images/aws04/15.png differ diff --git a/assets/images/aws04/16.png b/assets/images/aws04/16.png new file mode 100644 index 000000000000..24a47f1050dd Binary files /dev/null and b/assets/images/aws04/16.png differ diff --git a/assets/images/aws04/17.png b/assets/images/aws04/17.png new file mode 100644 index 000000000000..e6fff3cda6fc Binary files /dev/null and b/assets/images/aws04/17.png differ diff --git a/assets/images/aws04/18.png b/assets/images/aws04/18.png new file mode 100644 index 000000000000..8eca004ddab7 Binary files /dev/null and b/assets/images/aws04/18.png differ diff --git a/assets/images/aws04/19.png b/assets/images/aws04/19.png new file mode 100644 index 000000000000..8b43d279ce4e Binary files /dev/null and b/assets/images/aws04/19.png differ diff --git a/assets/images/aws04/20.png b/assets/images/aws04/20.png new file mode 100644 index 000000000000..d202e99b0c2b Binary files /dev/null and b/assets/images/aws04/20.png differ diff --git a/assets/images/aws04/21.png b/assets/images/aws04/21.png new file mode 100644 index 000000000000..4e378d9aa71c Binary files /dev/null and b/assets/images/aws04/21.png differ diff --git a/assets/images/aws04/22.png b/assets/images/aws04/22.png new file mode 100644 index 000000000000..759543dc4283 Binary files /dev/null and b/assets/images/aws04/22.png differ diff --git a/assets/images/aws04/23.png b/assets/images/aws04/23.png new file mode 100644 index 000000000000..8a8405476eec Binary files /dev/null and b/assets/images/aws04/23.png differ diff --git a/assets/images/aws04/24.png b/assets/images/aws04/24.png new file mode 100644 index 000000000000..39b7fba5cbd1 Binary files /dev/null and b/assets/images/aws04/24.png differ diff --git a/assets/images/aws04/25.png b/assets/images/aws04/25.png new file mode 100644 index 000000000000..f0e74de6bd11 Binary files /dev/null and b/assets/images/aws04/25.png differ diff --git a/assets/images/aws04/26.png b/assets/images/aws04/26.png new file mode 100644 index 000000000000..99b7ad265676 Binary files /dev/null and b/assets/images/aws04/26.png differ diff --git a/assets/images/aws04/27.png b/assets/images/aws04/27.png new file mode 100644 index 000000000000..39e537c0959c Binary files /dev/null and b/assets/images/aws04/27.png differ diff --git a/assets/images/aws04/28.png b/assets/images/aws04/28.png new file mode 100644 index 000000000000..4df0bfc1c229 Binary files /dev/null and b/assets/images/aws04/28.png differ diff --git a/assets/images/aws04/29.png b/assets/images/aws04/29.png new file mode 100644 index 000000000000..401031309f9d Binary files /dev/null and b/assets/images/aws04/29.png differ diff --git a/assets/images/aws04/30.png b/assets/images/aws04/30.png new file mode 100644 index 000000000000..87666f43ce10 Binary files /dev/null and b/assets/images/aws04/30.png differ diff --git a/assets/images/aws04/31.png b/assets/images/aws04/31.png new file mode 100644 index 000000000000..07eed66ebef2 Binary files /dev/null and b/assets/images/aws04/31.png differ diff --git a/assets/images/aws04/32.png b/assets/images/aws04/32.png new file mode 100644 index 000000000000..b6070dd1f203 Binary files /dev/null and b/assets/images/aws04/32.png differ diff --git a/assets/images/aws04/33.png b/assets/images/aws04/33.png new file mode 100644 index 000000000000..4e74958cf99b Binary files /dev/null and b/assets/images/aws04/33.png differ diff --git a/assets/images/aws04/34.png b/assets/images/aws04/34.png new file mode 100644 index 000000000000..166ed48a076e Binary files /dev/null and b/assets/images/aws04/34.png differ diff --git a/assets/images/aws04/35.png b/assets/images/aws04/35.png new file mode 100644 index 000000000000..12d4c85216ff Binary files /dev/null and b/assets/images/aws04/35.png differ diff --git a/assets/images/aws04/36.png b/assets/images/aws04/36.png new file mode 100644 index 000000000000..a98debf0d498 Binary files /dev/null and b/assets/images/aws04/36.png differ diff --git a/assets/images/aws05/1.png b/assets/images/aws05/1.png new file mode 100644 index 000000000000..142897b0eb14 Binary files /dev/null and b/assets/images/aws05/1.png differ diff --git a/assets/images/aws06/1.png b/assets/images/aws06/1.png new file mode 100644 index 000000000000..2f482266d8af Binary files /dev/null and b/assets/images/aws06/1.png differ diff --git a/assets/images/aws06/2.png b/assets/images/aws06/2.png new file mode 100644 index 000000000000..e1a06937e120 Binary files /dev/null and b/assets/images/aws06/2.png differ diff --git a/assets/images/aws07/1.png b/assets/images/aws07/1.png new file mode 100644 index 000000000000..6d58e79034cd Binary files /dev/null and b/assets/images/aws07/1.png differ diff --git a/assets/images/aws07/10.png b/assets/images/aws07/10.png new file mode 100644 index 000000000000..e4ab75edd31f Binary files /dev/null and b/assets/images/aws07/10.png differ diff --git a/assets/images/aws07/11.png b/assets/images/aws07/11.png new file mode 100644 index 000000000000..9da53722cd04 Binary files /dev/null and b/assets/images/aws07/11.png differ diff --git a/assets/images/aws07/12.png b/assets/images/aws07/12.png new file mode 100644 index 000000000000..5888b1144f83 Binary files /dev/null and b/assets/images/aws07/12.png differ diff --git a/assets/images/aws07/13.png b/assets/images/aws07/13.png new file mode 100644 index 000000000000..90e173c2d490 Binary files /dev/null and b/assets/images/aws07/13.png differ diff --git a/assets/images/aws07/14.png b/assets/images/aws07/14.png new file mode 100644 index 000000000000..3a0c5d002bfb Binary files /dev/null and b/assets/images/aws07/14.png differ diff --git a/assets/images/aws07/15.png b/assets/images/aws07/15.png new file mode 100644 index 000000000000..17897c2cb72c Binary files /dev/null and b/assets/images/aws07/15.png differ diff --git a/assets/images/aws07/16.png b/assets/images/aws07/16.png new file mode 100644 index 000000000000..1fee2a06d406 Binary files /dev/null and b/assets/images/aws07/16.png differ diff --git a/assets/images/aws07/17.png b/assets/images/aws07/17.png new file mode 100644 index 000000000000..0ba178e80b99 Binary files /dev/null and b/assets/images/aws07/17.png differ diff --git a/assets/images/aws07/18.png b/assets/images/aws07/18.png new file mode 100644 index 000000000000..1762d5582800 Binary files /dev/null and b/assets/images/aws07/18.png differ diff --git a/assets/images/aws07/19.png b/assets/images/aws07/19.png new file mode 100644 index 000000000000..4833172d6838 Binary files /dev/null and b/assets/images/aws07/19.png differ diff --git a/assets/images/aws07/2.png b/assets/images/aws07/2.png new file mode 100644 index 000000000000..1fd5b26ce2b5 Binary files /dev/null and b/assets/images/aws07/2.png differ diff --git a/assets/images/aws07/20.png b/assets/images/aws07/20.png new file mode 100644 index 000000000000..d3506d3f7344 Binary files /dev/null and b/assets/images/aws07/20.png differ diff --git a/assets/images/aws07/3.png b/assets/images/aws07/3.png new file mode 100644 index 000000000000..5856a367771f Binary files /dev/null and b/assets/images/aws07/3.png differ diff --git a/assets/images/aws07/4.png b/assets/images/aws07/4.png new file mode 100644 index 000000000000..b6eae43d83d7 Binary files /dev/null and b/assets/images/aws07/4.png differ diff --git a/assets/images/aws07/5.png b/assets/images/aws07/5.png new file mode 100644 index 000000000000..482558826cc1 Binary files /dev/null and b/assets/images/aws07/5.png differ diff --git a/assets/images/aws07/6.png b/assets/images/aws07/6.png new file mode 100644 index 000000000000..d18e4d680787 Binary files /dev/null and b/assets/images/aws07/6.png differ diff --git a/assets/images/aws07/7.png b/assets/images/aws07/7.png new file mode 100644 index 000000000000..06e79dcb346c Binary files /dev/null and b/assets/images/aws07/7.png differ diff --git a/assets/images/aws07/8.png b/assets/images/aws07/8.png new file mode 100644 index 000000000000..7ddd7825ed39 Binary files /dev/null and b/assets/images/aws07/8.png differ diff --git a/assets/images/aws07/9.png b/assets/images/aws07/9.png new file mode 100644 index 000000000000..3f21629cd27f Binary files /dev/null and b/assets/images/aws07/9.png differ diff --git a/assets/images/aws08/2.46.02.png b/assets/images/aws08/2.46.02.png new file mode 100644 index 000000000000..6d120db4863d Binary files /dev/null and b/assets/images/aws08/2.46.02.png differ diff --git a/assets/images/aws08/2.51.27.png b/assets/images/aws08/2.51.27.png new file mode 100644 index 000000000000..c99d7af02b96 Binary files /dev/null and b/assets/images/aws08/2.51.27.png differ diff --git a/assets/images/aws08/2.54.49.png b/assets/images/aws08/2.54.49.png new file mode 100644 index 000000000000..708ccc069591 Binary files /dev/null and b/assets/images/aws08/2.54.49.png differ diff --git a/assets/images/aws08/3.04.03.png b/assets/images/aws08/3.04.03.png new file mode 100644 index 000000000000..1f84e0ff2c2d Binary files /dev/null and b/assets/images/aws08/3.04.03.png differ diff --git a/assets/images/aws09/9.13.09.png b/assets/images/aws09/9.13.09.png new file mode 100644 index 000000000000..690ff6391fac Binary files /dev/null and b/assets/images/aws09/9.13.09.png differ diff --git a/assets/images/aws10/1.02.58.png b/assets/images/aws10/1.02.58.png new file mode 100644 index 000000000000..739b6b8f4073 Binary files /dev/null and b/assets/images/aws10/1.02.58.png differ diff --git a/assets/images/aws10/1.05.12.png b/assets/images/aws10/1.05.12.png new file mode 100644 index 000000000000..ecff74045acd Binary files /dev/null and b/assets/images/aws10/1.05.12.png differ diff --git a/assets/images/aws10/1.23.32.png b/assets/images/aws10/1.23.32.png new file mode 100644 index 000000000000..49a0f8db72f4 Binary files /dev/null and b/assets/images/aws10/1.23.32.png differ diff --git a/assets/images/aws10/1.24.51.png b/assets/images/aws10/1.24.51.png new file mode 100644 index 000000000000..e0398efa1dcb Binary files /dev/null and b/assets/images/aws10/1.24.51.png differ diff --git a/assets/images/aws10/1.25.58.png b/assets/images/aws10/1.25.58.png new file mode 100644 index 000000000000..e5253bc97dd9 Binary files /dev/null and b/assets/images/aws10/1.25.58.png differ diff --git a/assets/images/aws10/1.27.53.png b/assets/images/aws10/1.27.53.png new file mode 100644 index 000000000000..bd92dff90f8c Binary files /dev/null and b/assets/images/aws10/1.27.53.png differ diff --git a/assets/images/aws10/1.28.53.png b/assets/images/aws10/1.28.53.png new file mode 100644 index 000000000000..c0435a6ea6bc Binary files /dev/null and b/assets/images/aws10/1.28.53.png differ diff --git a/assets/images/aws10/1.46.06.png b/assets/images/aws10/1.46.06.png new file mode 100644 index 000000000000..f6a254a3f336 Binary files /dev/null and b/assets/images/aws10/1.46.06.png differ diff --git a/assets/images/aws10/1.46.17.png b/assets/images/aws10/1.46.17.png new file mode 100644 index 000000000000..19744f63b90b Binary files /dev/null and b/assets/images/aws10/1.46.17.png differ diff --git a/assets/images/aws10/1.46.43.png b/assets/images/aws10/1.46.43.png new file mode 100644 index 000000000000..48fd8b36eb4d Binary files /dev/null and b/assets/images/aws10/1.46.43.png differ diff --git a/assets/images/aws10/1.47.56.png b/assets/images/aws10/1.47.56.png new file mode 100644 index 000000000000..0b017d7b6e3c Binary files /dev/null and b/assets/images/aws10/1.47.56.png differ diff --git a/assets/images/aws10/1.49.50.png b/assets/images/aws10/1.49.50.png new file mode 100644 index 000000000000..e8ca1388ac18 Binary files /dev/null and b/assets/images/aws10/1.49.50.png differ diff --git a/assets/images/aws10/1.54.48.png b/assets/images/aws10/1.54.48.png new file mode 100644 index 000000000000..88f995f756d9 Binary files /dev/null and b/assets/images/aws10/1.54.48.png differ diff --git a/assets/images/aws10/1.58.08.png b/assets/images/aws10/1.58.08.png new file mode 100644 index 000000000000..41cb76449c2b Binary files /dev/null and b/assets/images/aws10/1.58.08.png differ diff --git a/assets/images/aws10/1.59.35.png b/assets/images/aws10/1.59.35.png new file mode 100644 index 000000000000..76428259e912 Binary files /dev/null and b/assets/images/aws10/1.59.35.png differ diff --git a/assets/images/aws10/12.42.58.png b/assets/images/aws10/12.42.58.png new file mode 100644 index 000000000000..fbcc1d78ffa6 Binary files /dev/null and b/assets/images/aws10/12.42.58.png differ diff --git a/assets/images/aws10/2.01.07.png b/assets/images/aws10/2.01.07.png new file mode 100644 index 000000000000..5bfeeed4bdc4 Binary files /dev/null and b/assets/images/aws10/2.01.07.png differ diff --git a/assets/images/aws10/2.02.58.png b/assets/images/aws10/2.02.58.png new file mode 100644 index 000000000000..60abfd2ce358 Binary files /dev/null and b/assets/images/aws10/2.02.58.png differ diff --git a/assets/images/aws10/2.03.56.png b/assets/images/aws10/2.03.56.png new file mode 100644 index 000000000000..b3d0b43cf01c Binary files /dev/null and b/assets/images/aws10/2.03.56.png differ diff --git a/assets/images/aws11/3.13.41.png b/assets/images/aws11/3.13.41.png new file mode 100644 index 000000000000..8e49946cbedd Binary files /dev/null and b/assets/images/aws11/3.13.41.png differ diff --git a/assets/images/aws11/3.14.47.png b/assets/images/aws11/3.14.47.png new file mode 100644 index 000000000000..fff7283ed7b4 Binary files /dev/null and b/assets/images/aws11/3.14.47.png differ diff --git a/assets/images/aws11/3.16.46.png b/assets/images/aws11/3.16.46.png new file mode 100644 index 000000000000..9ae1cfe2997a Binary files /dev/null and b/assets/images/aws11/3.16.46.png differ diff --git a/assets/images/aws11/3.18.42.png b/assets/images/aws11/3.18.42.png new file mode 100644 index 000000000000..f68ee25cddb7 Binary files /dev/null and b/assets/images/aws11/3.18.42.png differ diff --git a/assets/images/aws11/3.20.48.png b/assets/images/aws11/3.20.48.png new file mode 100644 index 000000000000..e31a8da64e31 Binary files /dev/null and b/assets/images/aws11/3.20.48.png differ diff --git a/assets/images/aws11/3.23.24.png b/assets/images/aws11/3.23.24.png new file mode 100644 index 000000000000..ce72b77a9aa6 Binary files /dev/null and b/assets/images/aws11/3.23.24.png differ diff --git a/assets/images/aws11/3.27.02.png b/assets/images/aws11/3.27.02.png new file mode 100644 index 000000000000..9100a715ffc6 Binary files /dev/null and b/assets/images/aws11/3.27.02.png differ diff --git a/assets/images/aws11/3.31.08.png b/assets/images/aws11/3.31.08.png new file mode 100644 index 000000000000..6f2faec6db19 Binary files /dev/null and b/assets/images/aws11/3.31.08.png differ diff --git a/assets/images/aws11/3.45.10.png b/assets/images/aws11/3.45.10.png new file mode 100644 index 000000000000..4a1df26f3e19 Binary files /dev/null and b/assets/images/aws11/3.45.10.png differ diff --git a/assets/images/aws11/4.17.05.png b/assets/images/aws11/4.17.05.png new file mode 100644 index 000000000000..797d966ec587 Binary files /dev/null and b/assets/images/aws11/4.17.05.png differ diff --git a/assets/images/aws11/4.25.08.png b/assets/images/aws11/4.25.08.png new file mode 100644 index 000000000000..721e01113dd5 Binary files /dev/null and b/assets/images/aws11/4.25.08.png differ diff --git a/assets/images/aws11/4.25.24.png b/assets/images/aws11/4.25.24.png new file mode 100644 index 000000000000..d16884ee98ec Binary files /dev/null and b/assets/images/aws11/4.25.24.png differ diff --git a/assets/images/aws11/4.35.23.png b/assets/images/aws11/4.35.23.png new file mode 100644 index 000000000000..84c94941ba06 Binary files /dev/null and b/assets/images/aws11/4.35.23.png differ diff --git a/assets/images/aws11/4.39.09.png b/assets/images/aws11/4.39.09.png new file mode 100644 index 000000000000..e78af3125c6d Binary files /dev/null and b/assets/images/aws11/4.39.09.png differ diff --git a/assets/images/aws11/4.49.32.png b/assets/images/aws11/4.49.32.png new file mode 100644 index 000000000000..e9c1c95f56f3 Binary files /dev/null and b/assets/images/aws11/4.49.32.png differ diff --git a/assets/images/aws11/4.52.09.png b/assets/images/aws11/4.52.09.png new file mode 100644 index 000000000000..40f750ab66e2 Binary files /dev/null and b/assets/images/aws11/4.52.09.png differ diff --git a/assets/images/aws11/4.57.29.png b/assets/images/aws11/4.57.29.png new file mode 100644 index 000000000000..ebabd665f2d6 Binary files /dev/null and b/assets/images/aws11/4.57.29.png differ diff --git a/assets/images/aws11/4.58.18.png b/assets/images/aws11/4.58.18.png new file mode 100644 index 000000000000..2c7386097fea Binary files /dev/null and b/assets/images/aws11/4.58.18.png differ diff --git a/assets/images/aws12/5.34.33.png b/assets/images/aws12/5.34.33.png new file mode 100644 index 000000000000..807273c4d7fb Binary files /dev/null and b/assets/images/aws12/5.34.33.png differ diff --git a/assets/images/aws12/5.43.05.png b/assets/images/aws12/5.43.05.png new file mode 100644 index 000000000000..f6cf4190884f Binary files /dev/null and b/assets/images/aws12/5.43.05.png differ diff --git a/assets/images/aws12/6.47.27.png b/assets/images/aws12/6.47.27.png new file mode 100644 index 000000000000..34f7049c1d36 Binary files /dev/null and b/assets/images/aws12/6.47.27.png differ diff --git a/assets/images/aws12/7.56.23.png b/assets/images/aws12/7.56.23.png new file mode 100644 index 000000000000..2c76be717a50 Binary files /dev/null and b/assets/images/aws12/7.56.23.png differ diff --git a/assets/images/git01/01.png b/assets/images/git01/01.png new file mode 100644 index 000000000000..78a6f0d6724e Binary files /dev/null and b/assets/images/git01/01.png differ diff --git a/assets/images/git01/02.png b/assets/images/git01/02.png new file mode 100644 index 000000000000..d05c3e51879f Binary files /dev/null and b/assets/images/git01/02.png differ diff --git a/assets/images/git01/03.png b/assets/images/git01/03.png new file mode 100644 index 000000000000..8bd8862ccad1 Binary files /dev/null and b/assets/images/git01/03.png differ diff --git a/assets/images/git01/04.png b/assets/images/git01/04.png new file mode 100644 index 000000000000..a2df38409840 Binary files /dev/null and b/assets/images/git01/04.png differ diff --git a/assets/images/git01/05.png b/assets/images/git01/05.png new file mode 100644 index 000000000000..6d50148fbdcd Binary files /dev/null and b/assets/images/git01/05.png differ diff --git a/assets/images/git01/06.png b/assets/images/git01/06.png new file mode 100644 index 000000000000..e44dc307dfa8 Binary files /dev/null and b/assets/images/git01/06.png differ diff --git a/assets/images/ml01/01.png b/assets/images/ml01/01.png new file mode 100644 index 000000000000..984e1d40e74e Binary files /dev/null and b/assets/images/ml01/01.png differ diff --git a/assets/images/ml01/02.png b/assets/images/ml01/02.png new file mode 100644 index 000000000000..0ee416d8c61a Binary files /dev/null and b/assets/images/ml01/02.png differ diff --git a/assets/images/ml02/01.png b/assets/images/ml02/01.png new file mode 100644 index 000000000000..03f43ce97b83 Binary files /dev/null and b/assets/images/ml02/01.png differ diff --git a/assets/images/ml02/02.png b/assets/images/ml02/02.png new file mode 100644 index 000000000000..6b9cb1a745b6 Binary files /dev/null and b/assets/images/ml02/02.png differ diff --git a/assets/images/ml03/01.png b/assets/images/ml03/01.png new file mode 100644 index 000000000000..d46c1d4b120e Binary files /dev/null and b/assets/images/ml03/01.png differ diff --git a/assets/images/ml03/02.png b/assets/images/ml03/02.png new file mode 100644 index 000000000000..9da015d9730f Binary files /dev/null and b/assets/images/ml03/02.png differ diff --git a/assets/images/ml03/output_20_0.svg b/assets/images/ml03/output_20_0.svg new file mode 100644 index 000000000000..48e0ac096b9e --- /dev/null +++ b/assets/images/ml03/output_20_0.svg @@ -0,0 +1,396 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" + "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<!-- Generated by graphviz version 2.44.2~dev.20201112.1525 (20201112.1525) + --> +<!-- Title: Tree Pages: 1 --> +<svg width="1066pt" height="790pt" + viewBox="0.00 0.00 1066.00 790.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 786)"> +<title>Tree</title> +<polygon fill="white" stroke="transparent" points="-4,4 -4,-786 1062,-786 1062,4 -4,4"/> +<!-- 0 --> +<g id="node1" class="node"> +<title>0</title> +<path fill="#fdf3ed" stroke="black" d="M668.5,-782C668.5,-782 546.5,-782 546.5,-782 540.5,-782 534.5,-776 534.5,-770 534.5,-770 534.5,-711 534.5,-711 534.5,-705 540.5,-699 546.5,-699 546.5,-699 668.5,-699 668.5,-699 674.5,-699 680.5,-705 680.5,-711 680.5,-711 680.5,-770 680.5,-770 680.5,-776 674.5,-782 668.5,-782"/> +<text text-anchor="middle" x="607.5" y="-766.8" font-family="Helvetica,sans-Serif" font-size="14.00">odor_n <= 0.5</text> +<text text-anchor="middle" x="607.5" y="-751.8" font-family="Helvetica,sans-Serif" font-size="14.00">gini = 0.499</text> +<text text-anchor="middle" x="607.5" y="-736.8" font-family="Helvetica,sans-Serif" font-size="14.00">samples = 5686</text> +<text text-anchor="middle" x="607.5" y="-721.8" font-family="Helvetica,sans-Serif" font-size="14.00">value = [2980, 2706]</text> +<text text-anchor="middle" x="607.5" y="-706.8" font-family="Helvetica,sans-Serif" font-size="14.00">class = 독</text> +</g> +<!-- 1 --> +<g id="node2" class="node"> +<title>1</title> +<path fill="#63b2eb" stroke="black" d="M568.5,-663C568.5,-663 454.5,-663 454.5,-663 448.5,-663 442.5,-657 442.5,-651 442.5,-651 442.5,-592 442.5,-592 442.5,-586 448.5,-580 454.5,-580 454.5,-580 568.5,-580 568.5,-580 574.5,-580 580.5,-586 580.5,-592 580.5,-592 580.5,-651 580.5,-651 580.5,-657 574.5,-663 568.5,-663"/> +<text text-anchor="middle" x="511.5" y="-647.8" font-family="Helvetica,sans-Serif" font-size="14.00">stalk-root_c <= 0.5</text> +<text text-anchor="middle" x="511.5" y="-632.8" font-family="Helvetica,sans-Serif" font-size="14.00">gini = 0.29</text> +<text text-anchor="middle" x="511.5" y="-617.8" font-family="Helvetica,sans-Serif" font-size="14.00">samples = 3175</text> +<text text-anchor="middle" x="511.5" y="-602.8" font-family="Helvetica,sans-Serif" font-size="14.00">value = [558, 2617]</text> +<text text-anchor="middle" x="511.5" y="-587.8" font-family="Helvetica,sans-Serif" font-size="14.00">class = 식용</text> +</g> +<!-- 0->1 --> +<g id="edge1" class="edge"> +<title>0:->1:</title> +<path fill="none" stroke="black" d="M574.19,-698.91C566.75,-689.83 558.78,-680.12 551.11,-670.77"/> +<polygon fill="black" stroke="black" points="553.8,-668.53 544.75,-663.02 548.39,-672.97 553.8,-668.53"/> +<text text-anchor="middle" x="542.29" y="-684.2" font-family="Helvetica,sans-Serif" font-size="14.00">True</text> +</g> +<!-- 12 --> +<g id="node13" class="node"> +<title>12</title> +<path fill="#e68640" stroke="black" d="M812.5,-663C812.5,-663 658.5,-663 658.5,-663 652.5,-663 646.5,-657 646.5,-651 646.5,-651 646.5,-592 646.5,-592 646.5,-586 652.5,-580 658.5,-580 658.5,-580 812.5,-580 812.5,-580 818.5,-580 824.5,-586 824.5,-592 824.5,-592 824.5,-651 824.5,-651 824.5,-657 818.5,-663 812.5,-663"/> +<text text-anchor="middle" x="735.5" y="-647.8" font-family="Helvetica,sans-Serif" font-size="14.00">spore-print-color_r <= 0.5</text> +<text text-anchor="middle" x="735.5" y="-632.8" font-family="Helvetica,sans-Serif" font-size="14.00">gini = 0.068</text> +<text text-anchor="middle" x="735.5" y="-617.8" font-family="Helvetica,sans-Serif" font-size="14.00">samples = 2511</text> +<text text-anchor="middle" x="735.5" y="-602.8" font-family="Helvetica,sans-Serif" font-size="14.00">value = [2422, 89]</text> +<text text-anchor="middle" x="735.5" y="-587.8" font-family="Helvetica,sans-Serif" font-size="14.00">class = 독</text> +</g> +<!-- 0->12 --> +<g id="edge12" class="edge"> +<title>0:->12:</title> +<path fill="none" stroke="black" d="M651.91,-698.91C662.14,-689.56 673.1,-679.54 683.61,-669.93"/> +<polygon fill="black" stroke="black" points="686.15,-672.35 691.17,-663.02 681.43,-667.18 686.15,-672.35"/> +<text text-anchor="middle" x="690.05" y="-684.3" font-family="Helvetica,sans-Serif" font-size="14.00">False</text> +</g> +<!-- 2 --> +<g id="node3" class="node"> +<title>2</title> +<path fill="#49a5e7" stroke="black" d="M387.5,-544C387.5,-544 273.5,-544 273.5,-544 267.5,-544 261.5,-538 261.5,-532 261.5,-532 261.5,-473 261.5,-473 261.5,-467 267.5,-461 273.5,-461 273.5,-461 387.5,-461 387.5,-461 393.5,-461 399.5,-467 399.5,-473 399.5,-473 399.5,-532 399.5,-532 399.5,-538 393.5,-544 387.5,-544"/> +<text text-anchor="middle" x="330.5" y="-528.8" font-family="Helvetica,sans-Serif" font-size="14.00">stalk-root_r <= 0.5</text> +<text text-anchor="middle" x="330.5" y="-513.8" font-family="Helvetica,sans-Serif" font-size="14.00">gini = 0.142</text> +<text text-anchor="middle" x="330.5" y="-498.8" font-family="Helvetica,sans-Serif" font-size="14.00">samples = 2811</text> +<text text-anchor="middle" x="330.5" y="-483.8" font-family="Helvetica,sans-Serif" font-size="14.00">value = [216, 2595]</text> +<text text-anchor="middle" x="330.5" y="-468.8" font-family="Helvetica,sans-Serif" font-size="14.00">class = 식용</text> +</g> +<!-- 1->2 --> +<g id="edge2" class="edge"> +<title>1:->2:</title> +<path fill="none" stroke="black" d="M448.7,-579.91C433.54,-570.11 417.23,-559.56 401.71,-549.53"/> +<polygon fill="black" stroke="black" points="403.48,-546.51 393.19,-544.02 399.68,-552.39 403.48,-546.51"/> +</g> +<!-- 9 --> +<g id="node10" class="node"> +<title>9</title> +<path fill="#e78946" stroke="black" d="M560.5,-544C560.5,-544 462.5,-544 462.5,-544 456.5,-544 450.5,-538 450.5,-532 450.5,-532 450.5,-473 450.5,-473 450.5,-467 456.5,-461 462.5,-461 462.5,-461 560.5,-461 560.5,-461 566.5,-461 572.5,-467 572.5,-473 572.5,-473 572.5,-532 572.5,-532 572.5,-538 566.5,-544 560.5,-544"/> +<text text-anchor="middle" x="511.5" y="-528.8" font-family="Helvetica,sans-Serif" font-size="14.00">odor_m <= 0.5</text> +<text text-anchor="middle" x="511.5" y="-513.8" font-family="Helvetica,sans-Serif" font-size="14.00">gini = 0.114</text> +<text text-anchor="middle" x="511.5" y="-498.8" font-family="Helvetica,sans-Serif" font-size="14.00">samples = 364</text> +<text text-anchor="middle" x="511.5" y="-483.8" font-family="Helvetica,sans-Serif" font-size="14.00">value = [342, 22]</text> +<text text-anchor="middle" x="511.5" y="-468.8" font-family="Helvetica,sans-Serif" font-size="14.00">class = 독</text> +</g> +<!-- 1->9 --> +<g id="edge9" class="edge"> +<title>1:->9:</title> +<path fill="none" stroke="black" d="M511.5,-579.91C511.5,-571.65 511.5,-562.86 511.5,-554.3"/> +<polygon fill="black" stroke="black" points="515,-554.02 511.5,-544.02 508,-554.02 515,-554.02"/> +</g> +<!-- 3 --> +<g id="node4" class="node"> +<title>3</title> +<path fill="#3ea0e6" stroke="black" d="M243.5,-425C243.5,-425 119.5,-425 119.5,-425 113.5,-425 107.5,-419 107.5,-413 107.5,-413 107.5,-354 107.5,-354 107.5,-348 113.5,-342 119.5,-342 119.5,-342 243.5,-342 243.5,-342 249.5,-342 255.5,-348 255.5,-354 255.5,-354 255.5,-413 255.5,-413 255.5,-419 249.5,-425 243.5,-425"/> +<text text-anchor="middle" x="181.5" y="-409.8" font-family="Helvetica,sans-Serif" font-size="14.00">gill-spacing_c <= 0.5</text> +<text text-anchor="middle" x="181.5" y="-394.8" font-family="Helvetica,sans-Serif" font-size="14.00">gini = 0.05</text> +<text text-anchor="middle" x="181.5" y="-379.8" font-family="Helvetica,sans-Serif" font-size="14.00">samples = 2664</text> +<text text-anchor="middle" x="181.5" y="-364.8" font-family="Helvetica,sans-Serif" font-size="14.00">value = [69, 2595]</text> +<text text-anchor="middle" x="181.5" y="-349.8" font-family="Helvetica,sans-Serif" font-size="14.00">class = 식용</text> +</g> +<!-- 2->3 --> +<g id="edge3" class="edge"> +<title>2:->3:</title> +<path fill="none" stroke="black" d="M278.81,-460.91C266.67,-451.38 253.64,-441.15 241.19,-431.37"/> +<polygon fill="black" stroke="black" points="243.13,-428.44 233.1,-425.02 238.81,-433.95 243.13,-428.44"/> +</g> +<!-- 8 --> +<g id="node9" class="node"> +<title>8</title> +<path fill="#e58139" stroke="black" d="M375,-417.5C375,-417.5 286,-417.5 286,-417.5 280,-417.5 274,-411.5 274,-405.5 274,-405.5 274,-361.5 274,-361.5 274,-355.5 280,-349.5 286,-349.5 286,-349.5 375,-349.5 375,-349.5 381,-349.5 387,-355.5 387,-361.5 387,-361.5 387,-405.5 387,-405.5 387,-411.5 381,-417.5 375,-417.5"/> +<text text-anchor="middle" x="330.5" y="-402.3" font-family="Helvetica,sans-Serif" font-size="14.00">gini = 0.0</text> +<text text-anchor="middle" x="330.5" y="-387.3" font-family="Helvetica,sans-Serif" font-size="14.00">samples = 147</text> +<text text-anchor="middle" x="330.5" y="-372.3" font-family="Helvetica,sans-Serif" font-size="14.00">value = [147, 0]</text> +<text text-anchor="middle" x="330.5" y="-357.3" font-family="Helvetica,sans-Serif" font-size="14.00">class = 독</text> +</g> +<!-- 2->8 --> +<g id="edge8" class="edge"> +<title>2:->8:</title> +<path fill="none" stroke="black" d="M330.5,-460.91C330.5,-450.2 330.5,-438.62 330.5,-427.78"/> +<polygon fill="black" stroke="black" points="334,-427.67 330.5,-417.67 327,-427.67 334,-427.67"/> +</g> +<!-- 4 --> +<g id="node5" class="node"> +<title>4</title> +<path fill="#fdf4ee" stroke="black" d="M158,-306C158,-306 69,-306 69,-306 63,-306 57,-300 57,-294 57,-294 57,-235 57,-235 57,-229 63,-223 69,-223 69,-223 158,-223 158,-223 164,-223 170,-229 170,-235 170,-235 170,-294 170,-294 170,-300 164,-306 158,-306"/> +<text text-anchor="middle" x="113.5" y="-290.8" font-family="Helvetica,sans-Serif" font-size="14.00">odor_c <= 0.5</text> +<text text-anchor="middle" x="113.5" y="-275.8" font-family="Helvetica,sans-Serif" font-size="14.00">gini = 0.499</text> +<text text-anchor="middle" x="113.5" y="-260.8" font-family="Helvetica,sans-Serif" font-size="14.00">samples = 132</text> +<text text-anchor="middle" x="113.5" y="-245.8" font-family="Helvetica,sans-Serif" font-size="14.00">value = [69, 63]</text> +<text text-anchor="middle" x="113.5" y="-230.8" font-family="Helvetica,sans-Serif" font-size="14.00">class = 독</text> +</g> +<!-- 3->4 --> +<g id="edge4" class="edge"> +<title>3:->4:</title> +<path fill="none" stroke="black" d="M157.91,-341.91C152.84,-333.2 147.44,-323.9 142.21,-314.89"/> +<polygon fill="black" stroke="black" points="145.1,-312.91 137.05,-306.02 139.05,-316.43 145.1,-312.91"/> +</g> +<!-- 7 --> +<g id="node8" class="node"> +<title>7</title> +<path fill="#399de5" stroke="black" d="M298.5,-298.5C298.5,-298.5 200.5,-298.5 200.5,-298.5 194.5,-298.5 188.5,-292.5 188.5,-286.5 188.5,-286.5 188.5,-242.5 188.5,-242.5 188.5,-236.5 194.5,-230.5 200.5,-230.5 200.5,-230.5 298.5,-230.5 298.5,-230.5 304.5,-230.5 310.5,-236.5 310.5,-242.5 310.5,-242.5 310.5,-286.5 310.5,-286.5 310.5,-292.5 304.5,-298.5 298.5,-298.5"/> +<text text-anchor="middle" x="249.5" y="-283.3" font-family="Helvetica,sans-Serif" font-size="14.00">gini = 0.0</text> +<text text-anchor="middle" x="249.5" y="-268.3" font-family="Helvetica,sans-Serif" font-size="14.00">samples = 2532</text> +<text text-anchor="middle" x="249.5" y="-253.3" font-family="Helvetica,sans-Serif" font-size="14.00">value = [0, 2532]</text> +<text text-anchor="middle" x="249.5" y="-238.3" font-family="Helvetica,sans-Serif" font-size="14.00">class = 식용</text> +</g> +<!-- 3->7 --> +<g id="edge7" class="edge"> +<title>3:->7:</title> +<path fill="none" stroke="black" d="M205.09,-341.91C211.57,-330.76 218.6,-318.66 225.13,-307.44"/> +<polygon fill="black" stroke="black" points="228.22,-309.07 230.22,-298.67 222.17,-305.55 228.22,-309.07"/> +</g> +<!-- 5 --> +<g id="node6" class="node"> +<title>5</title> +<path fill="#e58139" stroke="black" d="M93,-179.5C93,-179.5 12,-179.5 12,-179.5 6,-179.5 0,-173.5 0,-167.5 0,-167.5 0,-123.5 0,-123.5 0,-117.5 6,-111.5 12,-111.5 12,-111.5 93,-111.5 93,-111.5 99,-111.5 105,-117.5 105,-123.5 105,-123.5 105,-167.5 105,-167.5 105,-173.5 99,-179.5 93,-179.5"/> +<text text-anchor="middle" x="52.5" y="-164.3" font-family="Helvetica,sans-Serif" font-size="14.00">gini = 0.0</text> +<text text-anchor="middle" x="52.5" y="-149.3" font-family="Helvetica,sans-Serif" font-size="14.00">samples = 69</text> +<text text-anchor="middle" x="52.5" y="-134.3" font-family="Helvetica,sans-Serif" font-size="14.00">value = [69, 0]</text> +<text text-anchor="middle" x="52.5" y="-119.3" font-family="Helvetica,sans-Serif" font-size="14.00">class = 독</text> +</g> +<!-- 4->5 --> +<g id="edge5" class="edge"> +<title>4:->5:</title> +<path fill="none" stroke="black" d="M92.34,-222.91C86.58,-211.87 80.34,-199.9 74.54,-188.77"/> +<polygon fill="black" stroke="black" points="77.52,-186.92 69.79,-179.67 71.31,-190.15 77.52,-186.92"/> +</g> +<!-- 6 --> +<g id="node7" class="node"> +<title>6</title> +<path fill="#399de5" stroke="black" d="M216,-179.5C216,-179.5 135,-179.5 135,-179.5 129,-179.5 123,-173.5 123,-167.5 123,-167.5 123,-123.5 123,-123.5 123,-117.5 129,-111.5 135,-111.5 135,-111.5 216,-111.5 216,-111.5 222,-111.5 228,-117.5 228,-123.5 228,-123.5 228,-167.5 228,-167.5 228,-173.5 222,-179.5 216,-179.5"/> +<text text-anchor="middle" x="175.5" y="-164.3" font-family="Helvetica,sans-Serif" font-size="14.00">gini = 0.0</text> +<text text-anchor="middle" x="175.5" y="-149.3" font-family="Helvetica,sans-Serif" font-size="14.00">samples = 63</text> +<text text-anchor="middle" x="175.5" y="-134.3" font-family="Helvetica,sans-Serif" font-size="14.00">value = [0, 63]</text> +<text text-anchor="middle" x="175.5" y="-119.3" font-family="Helvetica,sans-Serif" font-size="14.00">class = 식용</text> +</g> +<!-- 4->6 --> +<g id="edge6" class="edge"> +<title>4:->6:</title> +<path fill="none" stroke="black" d="M135.01,-222.91C140.86,-211.87 147.2,-199.9 153.1,-188.77"/> +<polygon fill="black" stroke="black" points="156.33,-190.14 157.92,-179.67 150.15,-186.86 156.33,-190.14"/> +</g> +<!-- 10 --> +<g id="node11" class="node"> +<title>10</title> +<path fill="#e58139" stroke="black" d="M506,-417.5C506,-417.5 417,-417.5 417,-417.5 411,-417.5 405,-411.5 405,-405.5 405,-405.5 405,-361.5 405,-361.5 405,-355.5 411,-349.5 417,-349.5 417,-349.5 506,-349.5 506,-349.5 512,-349.5 518,-355.5 518,-361.5 518,-361.5 518,-405.5 518,-405.5 518,-411.5 512,-417.5 506,-417.5"/> +<text text-anchor="middle" x="461.5" y="-402.3" font-family="Helvetica,sans-Serif" font-size="14.00">gini = 0.0</text> +<text text-anchor="middle" x="461.5" y="-387.3" font-family="Helvetica,sans-Serif" font-size="14.00">samples = 342</text> +<text text-anchor="middle" x="461.5" y="-372.3" font-family="Helvetica,sans-Serif" font-size="14.00">value = [342, 0]</text> +<text text-anchor="middle" x="461.5" y="-357.3" font-family="Helvetica,sans-Serif" font-size="14.00">class = 독</text> +</g> +<!-- 9->10 --> +<g id="edge10" class="edge"> +<title>9:->10:</title> +<path fill="none" stroke="black" d="M494.15,-460.91C489.48,-449.98 484.42,-438.14 479.71,-427.11"/> +<polygon fill="black" stroke="black" points="482.82,-425.49 475.67,-417.67 476.39,-428.24 482.82,-425.49"/> +</g> +<!-- 11 --> +<g id="node12" class="node"> +<title>11</title> +<path fill="#399de5" stroke="black" d="M629,-417.5C629,-417.5 548,-417.5 548,-417.5 542,-417.5 536,-411.5 536,-405.5 536,-405.5 536,-361.5 536,-361.5 536,-355.5 542,-349.5 548,-349.5 548,-349.5 629,-349.5 629,-349.5 635,-349.5 641,-355.5 641,-361.5 641,-361.5 641,-405.5 641,-405.5 641,-411.5 635,-417.5 629,-417.5"/> +<text text-anchor="middle" x="588.5" y="-402.3" font-family="Helvetica,sans-Serif" font-size="14.00">gini = 0.0</text> +<text text-anchor="middle" x="588.5" y="-387.3" font-family="Helvetica,sans-Serif" font-size="14.00">samples = 22</text> +<text text-anchor="middle" x="588.5" y="-372.3" font-family="Helvetica,sans-Serif" font-size="14.00">value = [0, 22]</text> +<text text-anchor="middle" x="588.5" y="-357.3" font-family="Helvetica,sans-Serif" font-size="14.00">class = 식용</text> +</g> +<!-- 9->11 --> +<g id="edge11" class="edge"> +<title>9:->11:</title> +<path fill="none" stroke="black" d="M538.21,-460.91C545.62,-449.65 553.67,-437.42 561.12,-426.11"/> +<polygon fill="black" stroke="black" points="564.1,-427.94 566.67,-417.67 558.25,-424.1 564.1,-427.94"/> +</g> +<!-- 13 --> +<g id="node14" class="node"> +<title>13</title> +<path fill="#e5833c" stroke="black" d="M836.5,-544C836.5,-544 634.5,-544 634.5,-544 628.5,-544 622.5,-538 622.5,-532 622.5,-532 622.5,-473 622.5,-473 622.5,-467 628.5,-461 634.5,-461 634.5,-461 836.5,-461 836.5,-461 842.5,-461 848.5,-467 848.5,-473 848.5,-473 848.5,-532 848.5,-532 848.5,-538 842.5,-544 836.5,-544"/> +<text text-anchor="middle" x="735.5" y="-528.8" font-family="Helvetica,sans-Serif" font-size="14.00">stalk-surface-below-ring_y <= 0.5</text> +<text text-anchor="middle" x="735.5" y="-513.8" font-family="Helvetica,sans-Serif" font-size="14.00">gini = 0.03</text> +<text text-anchor="middle" x="735.5" y="-498.8" font-family="Helvetica,sans-Serif" font-size="14.00">samples = 2460</text> +<text text-anchor="middle" x="735.5" y="-483.8" font-family="Helvetica,sans-Serif" font-size="14.00">value = [2422, 38]</text> +<text text-anchor="middle" x="735.5" y="-468.8" font-family="Helvetica,sans-Serif" font-size="14.00">class = 독</text> +</g> +<!-- 12->13 --> +<g id="edge13" class="edge"> +<title>12:->13:</title> +<path fill="none" stroke="black" d="M735.5,-579.91C735.5,-571.65 735.5,-562.86 735.5,-554.3"/> +<polygon fill="black" stroke="black" points="739,-554.02 735.5,-544.02 732,-554.02 739,-554.02"/> +</g> +<!-- 24 --> +<g id="node25" class="node"> +<title>24</title> +<path fill="#399de5" stroke="black" d="M960,-536.5C960,-536.5 879,-536.5 879,-536.5 873,-536.5 867,-530.5 867,-524.5 867,-524.5 867,-480.5 867,-480.5 867,-474.5 873,-468.5 879,-468.5 879,-468.5 960,-468.5 960,-468.5 966,-468.5 972,-474.5 972,-480.5 972,-480.5 972,-524.5 972,-524.5 972,-530.5 966,-536.5 960,-536.5"/> +<text text-anchor="middle" x="919.5" y="-521.3" font-family="Helvetica,sans-Serif" font-size="14.00">gini = 0.0</text> +<text text-anchor="middle" x="919.5" y="-506.3" font-family="Helvetica,sans-Serif" font-size="14.00">samples = 51</text> +<text text-anchor="middle" x="919.5" y="-491.3" font-family="Helvetica,sans-Serif" font-size="14.00">value = [0, 51]</text> +<text text-anchor="middle" x="919.5" y="-476.3" font-family="Helvetica,sans-Serif" font-size="14.00">class = 식용</text> +</g> +<!-- 12->24 --> +<g id="edge24" class="edge"> +<title>12:->24:</title> +<path fill="none" stroke="black" d="M800.86,-579.83C819.22,-568.32 839.18,-555.73 857.5,-544 858.57,-543.32 859.64,-542.63 860.72,-541.93"/> +<polygon fill="black" stroke="black" points="862.62,-544.87 869.13,-536.51 858.83,-538.99 862.62,-544.87"/> +</g> +<!-- 14 --> +<g id="node15" class="node"> +<title>14</title> +<path fill="#e5813a" stroke="black" d="M799.5,-425C799.5,-425 671.5,-425 671.5,-425 665.5,-425 659.5,-419 659.5,-413 659.5,-413 659.5,-354 659.5,-354 659.5,-348 665.5,-342 671.5,-342 671.5,-342 799.5,-342 799.5,-342 805.5,-342 811.5,-348 811.5,-354 811.5,-354 811.5,-413 811.5,-413 811.5,-419 805.5,-425 799.5,-425"/> +<text text-anchor="middle" x="735.5" y="-409.8" font-family="Helvetica,sans-Serif" font-size="14.00">cap-surface_g <= 0.5</text> +<text text-anchor="middle" x="735.5" y="-394.8" font-family="Helvetica,sans-Serif" font-size="14.00">gini = 0.006</text> +<text text-anchor="middle" x="735.5" y="-379.8" font-family="Helvetica,sans-Serif" font-size="14.00">samples = 2419</text> +<text text-anchor="middle" x="735.5" y="-364.8" font-family="Helvetica,sans-Serif" font-size="14.00">value = [2412, 7]</text> +<text text-anchor="middle" x="735.5" y="-349.8" font-family="Helvetica,sans-Serif" font-size="14.00">class = 독</text> +</g> +<!-- 13->14 --> +<g id="edge14" class="edge"> +<title>13:->14:</title> +<path fill="none" stroke="black" d="M735.5,-460.91C735.5,-452.65 735.5,-443.86 735.5,-435.3"/> +<polygon fill="black" stroke="black" points="739,-435.02 735.5,-425.02 732,-435.02 739,-435.02"/> +</g> +<!-- 21 --> +<g id="node22" class="node"> +<title>21</title> +<path fill="#79bded" stroke="black" d="M941.5,-425C941.5,-425 841.5,-425 841.5,-425 835.5,-425 829.5,-419 829.5,-413 829.5,-413 829.5,-354 829.5,-354 829.5,-348 835.5,-342 841.5,-342 841.5,-342 941.5,-342 941.5,-342 947.5,-342 953.5,-348 953.5,-354 953.5,-354 953.5,-413 953.5,-413 953.5,-419 947.5,-425 941.5,-425"/> +<text text-anchor="middle" x="891.5" y="-409.8" font-family="Helvetica,sans-Serif" font-size="14.00">gill-size_n <= 0.5</text> +<text text-anchor="middle" x="891.5" y="-394.8" font-family="Helvetica,sans-Serif" font-size="14.00">gini = 0.369</text> +<text text-anchor="middle" x="891.5" y="-379.8" font-family="Helvetica,sans-Serif" font-size="14.00">samples = 41</text> +<text text-anchor="middle" x="891.5" y="-364.8" font-family="Helvetica,sans-Serif" font-size="14.00">value = [10, 31]</text> +<text text-anchor="middle" x="891.5" y="-349.8" font-family="Helvetica,sans-Serif" font-size="14.00">class = 식용</text> +</g> +<!-- 13->21 --> +<g id="edge21" class="edge"> +<title>13:->21:</title> +<path fill="none" stroke="black" d="M789.62,-460.91C802.45,-451.29 816.23,-440.95 829.38,-431.09"/> +<polygon fill="black" stroke="black" points="831.57,-433.82 837.47,-425.02 827.37,-428.22 831.57,-433.82"/> +</g> +<!-- 15 --> +<g id="node16" class="node"> +<title>15</title> +<path fill="#e58139" stroke="black" d="M664,-306C664,-306 563,-306 563,-306 557,-306 551,-300 551,-294 551,-294 551,-235 551,-235 551,-229 557,-223 563,-223 563,-223 664,-223 664,-223 670,-223 676,-229 676,-235 676,-235 676,-294 676,-294 676,-300 670,-306 664,-306"/> +<text text-anchor="middle" x="613.5" y="-290.8" font-family="Helvetica,sans-Serif" font-size="14.00">gill-size_b <= 0.5</text> +<text text-anchor="middle" x="613.5" y="-275.8" font-family="Helvetica,sans-Serif" font-size="14.00">gini = 0.002</text> +<text text-anchor="middle" x="613.5" y="-260.8" font-family="Helvetica,sans-Serif" font-size="14.00">samples = 2415</text> +<text text-anchor="middle" x="613.5" y="-245.8" font-family="Helvetica,sans-Serif" font-size="14.00">value = [2412, 3]</text> +<text text-anchor="middle" x="613.5" y="-230.8" font-family="Helvetica,sans-Serif" font-size="14.00">class = 독</text> +</g> +<!-- 14->15 --> +<g id="edge15" class="edge"> +<title>14:->15:</title> +<path fill="none" stroke="black" d="M693.17,-341.91C683.52,-332.65 673.18,-322.73 663.25,-313.21"/> +<polygon fill="black" stroke="black" points="665.39,-310.42 655.75,-306.02 660.55,-315.47 665.39,-310.42"/> +</g> +<!-- 20 --> +<g id="node21" class="node"> +<title>20</title> +<path fill="#399de5" stroke="black" d="M782.5,-298.5C782.5,-298.5 706.5,-298.5 706.5,-298.5 700.5,-298.5 694.5,-292.5 694.5,-286.5 694.5,-286.5 694.5,-242.5 694.5,-242.5 694.5,-236.5 700.5,-230.5 706.5,-230.5 706.5,-230.5 782.5,-230.5 782.5,-230.5 788.5,-230.5 794.5,-236.5 794.5,-242.5 794.5,-242.5 794.5,-286.5 794.5,-286.5 794.5,-292.5 788.5,-298.5 782.5,-298.5"/> +<text text-anchor="middle" x="744.5" y="-283.3" font-family="Helvetica,sans-Serif" font-size="14.00">gini = 0.0</text> +<text text-anchor="middle" x="744.5" y="-268.3" font-family="Helvetica,sans-Serif" font-size="14.00">samples = 4</text> +<text text-anchor="middle" x="744.5" y="-253.3" font-family="Helvetica,sans-Serif" font-size="14.00">value = [0, 4]</text> +<text text-anchor="middle" x="744.5" y="-238.3" font-family="Helvetica,sans-Serif" font-size="14.00">class = 식용</text> +</g> +<!-- 14->20 --> +<g id="edge20" class="edge"> +<title>14:->20:</title> +<path fill="none" stroke="black" d="M738.62,-341.91C739.45,-331.2 740.34,-319.62 741.17,-308.78"/> +<polygon fill="black" stroke="black" points="744.67,-308.91 741.95,-298.67 737.69,-308.37 744.67,-308.91"/> +</g> +<!-- 16 --> +<g id="node17" class="node"> +<title>16</title> +<path fill="#e6843d" stroke="black" d="M597.5,-187C597.5,-187 479.5,-187 479.5,-187 473.5,-187 467.5,-181 467.5,-175 467.5,-175 467.5,-116 467.5,-116 467.5,-110 473.5,-104 479.5,-104 479.5,-104 597.5,-104 597.5,-104 603.5,-104 609.5,-110 609.5,-116 609.5,-116 609.5,-175 609.5,-175 609.5,-181 603.5,-187 597.5,-187"/> +<text text-anchor="middle" x="538.5" y="-171.8" font-family="Helvetica,sans-Serif" font-size="14.00">population_c <= 0.5</text> +<text text-anchor="middle" x="538.5" y="-156.8" font-family="Helvetica,sans-Serif" font-size="14.00">gini = 0.042</text> +<text text-anchor="middle" x="538.5" y="-141.8" font-family="Helvetica,sans-Serif" font-size="14.00">samples = 139</text> +<text text-anchor="middle" x="538.5" y="-126.8" font-family="Helvetica,sans-Serif" font-size="14.00">value = [136, 3]</text> +<text text-anchor="middle" x="538.5" y="-111.8" font-family="Helvetica,sans-Serif" font-size="14.00">class = 독</text> +</g> +<!-- 15->16 --> +<g id="edge16" class="edge"> +<title>15:->16:</title> +<path fill="none" stroke="black" d="M587.48,-222.91C581.84,-214.1 575.81,-204.7 569.98,-195.61"/> +<polygon fill="black" stroke="black" points="572.82,-193.55 564.47,-187.02 566.93,-197.33 572.82,-193.55"/> +</g> +<!-- 19 --> +<g id="node20" class="node"> +<title>19</title> +<path fill="#e58139" stroke="black" d="M737.5,-179.5C737.5,-179.5 639.5,-179.5 639.5,-179.5 633.5,-179.5 627.5,-173.5 627.5,-167.5 627.5,-167.5 627.5,-123.5 627.5,-123.5 627.5,-117.5 633.5,-111.5 639.5,-111.5 639.5,-111.5 737.5,-111.5 737.5,-111.5 743.5,-111.5 749.5,-117.5 749.5,-123.5 749.5,-123.5 749.5,-167.5 749.5,-167.5 749.5,-173.5 743.5,-179.5 737.5,-179.5"/> +<text text-anchor="middle" x="688.5" y="-164.3" font-family="Helvetica,sans-Serif" font-size="14.00">gini = 0.0</text> +<text text-anchor="middle" x="688.5" y="-149.3" font-family="Helvetica,sans-Serif" font-size="14.00">samples = 2276</text> +<text text-anchor="middle" x="688.5" y="-134.3" font-family="Helvetica,sans-Serif" font-size="14.00">value = [2276, 0]</text> +<text text-anchor="middle" x="688.5" y="-119.3" font-family="Helvetica,sans-Serif" font-size="14.00">class = 독</text> +</g> +<!-- 15->19 --> +<g id="edge19" class="edge"> +<title>15:->19:</title> +<path fill="none" stroke="black" d="M639.52,-222.91C646.74,-211.65 654.58,-199.42 661.83,-188.11"/> +<polygon fill="black" stroke="black" points="664.79,-189.97 667.24,-179.67 658.9,-186.2 664.79,-189.97"/> +</g> +<!-- 17 --> +<g id="node18" class="node"> +<title>17</title> +<path fill="#e58139" stroke="black" d="M521,-68C521,-68 432,-68 432,-68 426,-68 420,-62 420,-56 420,-56 420,-12 420,-12 420,-6 426,0 432,0 432,0 521,0 521,0 527,0 533,-6 533,-12 533,-12 533,-56 533,-56 533,-62 527,-68 521,-68"/> +<text text-anchor="middle" x="476.5" y="-52.8" font-family="Helvetica,sans-Serif" font-size="14.00">gini = 0.0</text> +<text text-anchor="middle" x="476.5" y="-37.8" font-family="Helvetica,sans-Serif" font-size="14.00">samples = 136</text> +<text text-anchor="middle" x="476.5" y="-22.8" font-family="Helvetica,sans-Serif" font-size="14.00">value = [136, 0]</text> +<text text-anchor="middle" x="476.5" y="-7.8" font-family="Helvetica,sans-Serif" font-size="14.00">class = 독</text> +</g> +<!-- 16->17 --> +<g id="edge17" class="edge"> +<title>16:->17:</title> +<path fill="none" stroke="black" d="M515.41,-103.73C510.51,-95.06 505.32,-85.9 500.38,-77.18"/> +<polygon fill="black" stroke="black" points="503.33,-75.28 495.35,-68.3 497.24,-78.73 503.33,-75.28"/> +</g> +<!-- 18 --> +<g id="node19" class="node"> +<title>18</title> +<path fill="#399de5" stroke="black" d="M639.5,-68C639.5,-68 563.5,-68 563.5,-68 557.5,-68 551.5,-62 551.5,-56 551.5,-56 551.5,-12 551.5,-12 551.5,-6 557.5,0 563.5,0 563.5,0 639.5,0 639.5,0 645.5,0 651.5,-6 651.5,-12 651.5,-12 651.5,-56 651.5,-56 651.5,-62 645.5,-68 639.5,-68"/> +<text text-anchor="middle" x="601.5" y="-52.8" font-family="Helvetica,sans-Serif" font-size="14.00">gini = 0.0</text> +<text text-anchor="middle" x="601.5" y="-37.8" font-family="Helvetica,sans-Serif" font-size="14.00">samples = 3</text> +<text text-anchor="middle" x="601.5" y="-22.8" font-family="Helvetica,sans-Serif" font-size="14.00">value = [0, 3]</text> +<text text-anchor="middle" x="601.5" y="-7.8" font-family="Helvetica,sans-Serif" font-size="14.00">class = 식용</text> +</g> +<!-- 16->18 --> +<g id="edge18" class="edge"> +<title>16:->18:</title> +<path fill="none" stroke="black" d="M561.96,-103.73C566.94,-95.06 572.22,-85.9 577.23,-77.18"/> +<polygon fill="black" stroke="black" points="580.39,-78.71 582.34,-68.3 574.32,-75.22 580.39,-78.71"/> +</g> +<!-- 22 --> +<g id="node23" class="node"> +<title>22</title> +<path fill="#e58139" stroke="black" d="M923,-298.5C923,-298.5 842,-298.5 842,-298.5 836,-298.5 830,-292.5 830,-286.5 830,-286.5 830,-242.5 830,-242.5 830,-236.5 836,-230.5 842,-230.5 842,-230.5 923,-230.5 923,-230.5 929,-230.5 935,-236.5 935,-242.5 935,-242.5 935,-286.5 935,-286.5 935,-292.5 929,-298.5 923,-298.5"/> +<text text-anchor="middle" x="882.5" y="-283.3" font-family="Helvetica,sans-Serif" font-size="14.00">gini = 0.0</text> +<text text-anchor="middle" x="882.5" y="-268.3" font-family="Helvetica,sans-Serif" font-size="14.00">samples = 10</text> +<text text-anchor="middle" x="882.5" y="-253.3" font-family="Helvetica,sans-Serif" font-size="14.00">value = [10, 0]</text> +<text text-anchor="middle" x="882.5" y="-238.3" font-family="Helvetica,sans-Serif" font-size="14.00">class = 독</text> +</g> +<!-- 21->22 --> +<g id="edge22" class="edge"> +<title>21:->22:</title> +<path fill="none" stroke="black" d="M888.38,-341.91C887.55,-331.2 886.66,-319.62 885.83,-308.78"/> +<polygon fill="black" stroke="black" points="889.31,-308.37 885.05,-298.67 882.33,-308.91 889.31,-308.37"/> +</g> +<!-- 23 --> +<g id="node24" class="node"> +<title>23</title> +<path fill="#399de5" stroke="black" d="M1046,-298.5C1046,-298.5 965,-298.5 965,-298.5 959,-298.5 953,-292.5 953,-286.5 953,-286.5 953,-242.5 953,-242.5 953,-236.5 959,-230.5 965,-230.5 965,-230.5 1046,-230.5 1046,-230.5 1052,-230.5 1058,-236.5 1058,-242.5 1058,-242.5 1058,-286.5 1058,-286.5 1058,-292.5 1052,-298.5 1046,-298.5"/> +<text text-anchor="middle" x="1005.5" y="-283.3" font-family="Helvetica,sans-Serif" font-size="14.00">gini = 0.0</text> +<text text-anchor="middle" x="1005.5" y="-268.3" font-family="Helvetica,sans-Serif" font-size="14.00">samples = 31</text> +<text text-anchor="middle" x="1005.5" y="-253.3" font-family="Helvetica,sans-Serif" font-size="14.00">value = [0, 31]</text> +<text text-anchor="middle" x="1005.5" y="-238.3" font-family="Helvetica,sans-Serif" font-size="14.00">class = 식용</text> +</g> +<!-- 21->23 --> +<g id="edge23" class="edge"> +<title>21:->23:</title> +<path fill="none" stroke="black" d="M931.05,-341.91C942.34,-330.32 954.64,-317.7 965.93,-306.11"/> +<polygon fill="black" stroke="black" points="968.71,-308.27 973.18,-298.67 963.7,-303.39 968.71,-308.27"/> +</g> +</g> +</svg> diff --git a/assets/images/ml04/output_61_0.png b/assets/images/ml04/output_61_0.png new file mode 100644 index 000000000000..64939ca6457d Binary files /dev/null and b/assets/images/ml04/output_61_0.png differ diff --git a/assets/images/springboot01/00_1.png b/assets/images/springboot01/00_1.png new file mode 100644 index 000000000000..bb80801ee633 Binary files /dev/null and b/assets/images/springboot01/00_1.png differ diff --git a/assets/images/springboot01/01_1.png b/assets/images/springboot01/01_1.png new file mode 100644 index 000000000000..dbe7035149fd Binary files /dev/null and b/assets/images/springboot01/01_1.png differ diff --git a/assets/images/springboot02/00_1.png b/assets/images/springboot02/00_1.png new file mode 100644 index 000000000000..cc0213dae9e7 Binary files /dev/null and b/assets/images/springboot02/00_1.png differ diff --git a/assets/images/springboot03/00_1.png b/assets/images/springboot03/00_1.png new file mode 100644 index 000000000000..e0584ab96c05 Binary files /dev/null and b/assets/images/springboot03/00_1.png differ diff --git a/assets/images/springboot08/01.png b/assets/images/springboot08/01.png new file mode 100644 index 000000000000..ed32fbd23b64 Binary files /dev/null and b/assets/images/springboot08/01.png differ diff --git a/assets/images/springboot08/02.png b/assets/images/springboot08/02.png new file mode 100644 index 000000000000..c0e0e16af781 Binary files /dev/null and b/assets/images/springboot08/02.png differ diff --git a/assets/images/springboot08/03.png b/assets/images/springboot08/03.png new file mode 100644 index 000000000000..663dd8717810 Binary files /dev/null and b/assets/images/springboot08/03.png differ diff --git a/assets/images/springboot08/04.png b/assets/images/springboot08/04.png new file mode 100644 index 000000000000..8f2e5d73318a Binary files /dev/null and b/assets/images/springboot08/04.png differ diff --git a/assets/images/springboot08/05.png b/assets/images/springboot08/05.png new file mode 100644 index 000000000000..7e571a62d443 Binary files /dev/null and b/assets/images/springboot08/05.png differ diff --git a/assets/images/springboot08/06.png b/assets/images/springboot08/06.png new file mode 100644 index 000000000000..402aa070e7a9 Binary files /dev/null and b/assets/images/springboot08/06.png differ diff --git "a/assets/images/\352\262\275\354\202\254\355\225\230\352\260\225\353\262\225.png" "b/assets/images/\352\262\275\354\202\254\355\225\230\352\260\225\353\262\225.png" new file mode 100644 index 000000000000..cc5809644ce4 Binary files /dev/null and "b/assets/images/\352\262\275\354\202\254\355\225\230\352\260\225\353\262\225.png" differ diff --git "a/assets/images/\352\262\275\354\202\254\355\225\230\352\260\225\353\262\225_\352\263\274\354\240\225.png" "b/assets/images/\352\262\275\354\202\254\355\225\230\352\260\225\353\262\225_\352\263\274\354\240\225.png" new file mode 100644 index 000000000000..e8e5b0a800f4 Binary files /dev/null and "b/assets/images/\352\262\275\354\202\254\355\225\230\352\260\225\353\262\225_\352\263\274\354\240\225.png" differ diff --git "a/assets/images/\352\263\265\354\213\235_MSE.png" "b/assets/images/\352\263\265\354\213\235_MSE.png" new file mode 100644 index 000000000000..315f4f016420 Binary files /dev/null and "b/assets/images/\352\263\265\354\213\235_MSE.png" differ diff --git "a/assets/images/\352\263\265\354\213\235_\354\265\234\354\206\214\354\240\234\352\263\261\353\262\225.png" "b/assets/images/\352\263\265\354\213\235_\354\265\234\354\206\214\354\240\234\352\263\261\353\262\225.png" new file mode 100644 index 000000000000..516775f9c4db Binary files /dev/null and "b/assets/images/\352\263\265\354\213\235_\354\265\234\354\206\214\354\240\234\352\263\261\353\262\225.png" differ diff --git "a/assets/images/\353\246\254\355\204\264\354\213\234\355\200\270\354\212\244.PNG" "b/assets/images/\353\246\254\355\204\264\354\213\234\355\200\270\354\212\244.PNG" new file mode 100644 index 000000000000..ff015f94cbf7 Binary files /dev/null and "b/assets/images/\353\246\254\355\204\264\354\213\234\355\200\270\354\212\244.PNG" differ diff --git "a/assets/images/\353\247\245\354\212\244\355\222\200\353\247\201.png" "b/assets/images/\353\247\245\354\212\244\355\222\200\353\247\201.png" new file mode 100644 index 000000000000..7266182da7c4 Binary files /dev/null and "b/assets/images/\353\247\245\354\212\244\355\222\200\353\247\201.png" differ diff --git "a/assets/images/\353\252\250\353\221\220\354\235\230\353\224\245\353\237\254\353\213\235.png" "b/assets/images/\353\252\250\353\221\220\354\235\230\353\224\245\353\237\254\353\213\235.png" new file mode 100644 index 000000000000..431d79a6d0bc Binary files /dev/null and "b/assets/images/\353\252\250\353\221\220\354\235\230\353\224\245\353\237\254\353\213\235.png" differ diff --git "a/assets/images/\353\252\250\353\251\230\355\205\200.png" "b/assets/images/\353\252\250\353\251\230\355\205\200.png" new file mode 100644 index 000000000000..0be08065b09c Binary files /dev/null and "b/assets/images/\353\252\250\353\251\230\355\205\200.png" differ diff --git "a/assets/images/\354\213\234\352\267\270\353\252\250\354\235\264\353\223\234\353\257\270\353\266\204.png" "b/assets/images/\354\213\234\352\267\270\353\252\250\354\235\264\353\223\234\353\257\270\353\266\204.png" new file mode 100644 index 000000000000..debf33b19da7 Binary files /dev/null and "b/assets/images/\354\213\234\352\267\270\353\252\250\354\235\264\353\223\234\353\257\270\353\266\204.png" differ diff --git "a/assets/images/\354\230\244\354\260\250\354\227\255\354\240\204\355\214\2141.png" "b/assets/images/\354\230\244\354\260\250\354\227\255\354\240\204\355\214\2141.png" new file mode 100644 index 000000000000..2975e6e76087 Binary files /dev/null and "b/assets/images/\354\230\244\354\260\250\354\227\255\354\240\204\355\214\2141.png" differ diff --git "a/assets/images/\354\230\244\354\260\250\354\227\255\354\240\204\355\214\2142.png" "b/assets/images/\354\230\244\354\260\250\354\227\255\354\240\204\355\214\2142.png" new file mode 100644 index 000000000000..abec3e47f1ca Binary files /dev/null and "b/assets/images/\354\230\244\354\260\250\354\227\255\354\240\204\355\214\2142.png" differ diff --git "a/assets/images/\354\240\234\353\241\234\355\214\250\353\224\251.png" "b/assets/images/\354\240\234\353\241\234\355\214\250\353\224\251.png" new file mode 100644 index 000000000000..0bcc59971120 Binary files /dev/null and "b/assets/images/\354\240\234\353\241\234\355\214\250\353\224\251.png" differ diff --git "a/assets/images/\354\262\264\354\235\270\353\243\260.png" "b/assets/images/\354\262\264\354\235\270\353\243\260.png" new file mode 100644 index 000000000000..c94e3bc0fb89 Binary files /dev/null and "b/assets/images/\354\262\264\354\235\270\353\243\260.png" differ diff --git "a/assets/images/\355\215\274\354\205\211\355\212\270\353\241\240.png" "b/assets/images/\355\215\274\354\205\211\355\212\270\353\241\240.png" new file mode 100644 index 000000000000..aacf1507317e Binary files /dev/null and "b/assets/images/\355\215\274\354\205\211\355\212\270\353\241\240.png" differ diff --git "a/assets/images/\355\225\251\354\204\261\352\263\261\354\213\240\352\262\275\353\247\235_\354\227\254\353\237\254\352\260\234\354\235\230\355\225\204\355\204\260.png" "b/assets/images/\355\225\251\354\204\261\352\263\261\354\213\240\352\262\275\353\247\235_\354\227\254\353\237\254\352\260\234\354\235\230\355\225\204\355\204\260.png" new file mode 100644 index 000000000000..d60cc061e47b Binary files /dev/null and "b/assets/images/\355\225\251\354\204\261\352\263\261\354\213\240\352\262\275\353\247\235_\354\227\254\353\237\254\352\260\234\354\235\230\355\225\204\355\204\260.png" differ diff --git "a/assets/images/\355\225\251\354\204\261\352\263\261\354\213\240\352\262\275\353\247\235_\354\273\244\353\204\220.png" "b/assets/images/\355\225\251\354\204\261\352\263\261\354\213\240\352\262\275\353\247\235_\354\273\244\353\204\220.png" new file mode 100644 index 000000000000..20587b24b6ba Binary files /dev/null and "b/assets/images/\355\225\251\354\204\261\352\263\261\354\213\240\352\262\275\353\247\235_\354\273\244\353\204\220.png" differ diff --git "a/assets/images/\355\231\225\353\245\240\354\240\201_\352\262\275\354\202\254_\355\225\230\352\260\225\353\262\225.png" "b/assets/images/\355\231\225\353\245\240\354\240\201_\352\262\275\354\202\254_\355\225\230\352\260\225\353\262\225.png" new file mode 100644 index 000000000000..c2236b88f4e5 Binary files /dev/null and "b/assets/images/\355\231\225\353\245\240\354\240\201_\352\262\275\354\202\254_\355\225\230\352\260\225\353\262\225.png" differ diff --git "a/assets/images/\355\231\234\354\204\261\355\231\224\355\225\250\354\210\230_\355\205\214\354\235\264\353\270\224.png" "b/assets/images/\355\231\234\354\204\261\355\231\224\355\225\250\354\210\230_\355\205\214\354\235\264\353\270\224.png" new file mode 100644 index 000000000000..0016812b3269 Binary files /dev/null and "b/assets/images/\355\231\234\354\204\261\355\231\224\355\225\250\354\210\230_\355\205\214\354\235\264\353\270\224.png" differ diff --git a/docs/_config.yml b/docs/_config.yml index 2266a9047012..a8f2d2742063 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -10,7 +10,7 @@ remote_theme : "mmistakes/minimal-mistakes@master" minimal_mistakes_skin : "default" # "air", "aqua", "contrast", "dark", "dirt", "neon", "mint", "plum", "sunrise" # Site Settings -locale : "en-US" +locale : "ko-KR" # "en-US" title : "Minimal Mistakes" title_separator : "-" subtitle : "A Jekyll theme" @@ -26,9 +26,9 @@ masthead_title : # overrides the website title displayed in the masthe words_per_minute : 200 enable_copy_code_button : true comments: - provider : "false" # false (default), "disqus", "discourse", "facebook", "staticman_v2", "staticman", "utterances", "giscus", "custom" + provider : "disqus" # false (default), "disqus", "discourse", "facebook", "staticman_v2", "staticman", "utterances", "giscus", "custom" disqus: - shortname : + shortname : "yoonkienote" discourse: server : # https://meta.discourse.org/t/embedding-discourse-comments-via-javascript/31963 , e.g.: meta.discourse.org facebook: @@ -211,10 +211,11 @@ defaults: values: layout: single author_profile: true - read_time: true + read_time: false + show_date: true comments: true share: true - related: true + related: false # _pages - scope: path: "_pages" diff --git a/docs/_pages/category-archive.md b/docs/_pages/category-archive.md index 4cb3860e91be..6b7011e2ecf1 100644 --- a/docs/_pages/category-archive.md +++ b/docs/_pages/category-archive.md @@ -3,4 +3,5 @@ title: "Posts by Category" layout: categories permalink: /categories/ author_profile: true +sidebar_main: true --- diff --git a/google285e0ef783d2b56a.html b/google285e0ef783d2b56a.html new file mode 100644 index 000000000000..4e85c74a3dac --- /dev/null +++ b/google285e0ef783d2b56a.html @@ -0,0 +1 @@ +google-site-verification: google285e0ef783d2b56a.html \ No newline at end of file