no image
[딥러닝] CNN _ 도로 교통 표지판 인식
1. 데이터 가져오기 - 43개의 교통 표지판 과 관련된 4만여 개의 이미지로 만들어진 데이터 셋 - https://sid.erda.dk/public/archives/daaeac0d7ce1152aea9b61d9f1e19370/published-archive.html Public Archive: daaeac0d7ce1152aea9b61d9f1e19370 Support ERDA User Guide Questions about ERDA? Please contact us at support@erda.dk sid.erda.dk - https://drive.google.com/drive/folders/1AktNCWqVBv-4xxme4OUC82OLJTHzMFsq?usp=sharing - 데이터 다운로드 colab !..
2024.03.22
no image
[딥러닝] CNN _ 패션 이미지 분류
1. 데이터 가져오기 from tensorflow import keras from sklearn.model_selection import train_test_split #데이터 읽어오기 (train_input, train_target), (test_input, test_target) = keras.datasets.fashion_mnist.load_data() rint(train_input.shape) 2. 스케일링 및 차원 수정 # 흑백 이미지라서 차원을 1개 늘려서 255 로 나눔 train_scaled = train_input.reshape(-1, 28, 28, 1) / 255.0 print(train_scaled.shape) train_scaled, val_scaled, train_target, v..
2024.03.22
no image
[전처리] 인구 소멸 지역 Cartogram
- 국가 통계 포털에서 인구 정보 내려받기 http://kosis.kr/statHtml/statHtml.do?orgId=101&tblId=DT_1IN1509& KOSIS kosis.kr 기본코드 import pandas as pd import numpy as np import platform import matplotlib.pyplot as plt %matplotlib inline path = "c:/Windows/Fonts/malgun.ttf" from matplotlib import font_manager, rc if platform.system() == 'Darwin': rc('font', family='AppleGothic') elif platform.system() == 'Windows': fo..
2024.03.20
no image
[전처리] 서울시 범죄 현황 시각화
- https://data.seoul.go.kr/ - 서울시 5대 점죄 발생현황 통계 데이터 - crime.txt 1. 데이터 가져오기 import pandas as pd import folium import json criminal = pd.read_csv('./data/crime.txt', sep='\t', thousands=',', encoding='utf-8') 2. 데이터 전처리 # 불필요한 컬럼 제거 criminal.drop(['기간', '합계검거', '살인검거', '강도검거', '강간검거', '절도검거', '폭력검거'], axis=1, inplace=True) criminal.drop([0], inplace=True) # 컬럼 이름 설정 criminal.rename(columns={'살인발생..
2024.03.20
no image
[전처리] 지도 출력 Choropleth
https://github.com/southkorea/southkorea-maps 한국 지리정보 사이트 1. 데이터 읽어오기 state_unemployment = 'data/US_Unemployment_Oct2012.csv' state_data = pd.read_csv(state_unemployment) print(state_data.head()) State Unemployment 0 AL 7.1 1 AK 6.8 2 AZ 8.1 3 AR 7.2 4 CA 10.1 2. 단계 구분도 출력 folium.Map으로 만든 지도에 choropleth 함수 호출 geo_data 옵션에 아이디와 위도 및 경도를 가진 json 파일의 경로를 설정해주고 data에 출력한 DF 설정 key_on에 json 파일에서 사용되는..
2024.03.20
no image
[전처리] 서울시 구별 CCTV와 인구 관계 분석
1. 서울시 자치구별 CCTV 현황 데이터 가져오기 - http://data.seoul.go.kr/ 열린데이터광장 메인 데이터분류,데이터검색,데이터활용 data.seoul.go.kr - CCTV 검색 - xlsx 파일을 다운로드(파일 이름을 cctv로 수정) # 데이터 읽기 import matplotlib.pyplot as plt import pandas as pd import numpy as np from pandas import Series, DataFrame import platform from matplotlib import font_manager, rc #데이터 읽어오기 cctv = pd.read_excel('./data/cctv.xlsx') print(cctv.head()) cctv.info(..
2024.03.20
no image
[Python] 딥러닝 _ Keras
1. Keras 모든 종류의 신경망을 손쉽게 만들고 훈련, 평가, 실행할 수 있는 고수준 딥러닝 API API 문서는 https://keras.io 거의 모든 딥러닝 라이브러리에서 사용 가능 2. Keras 의 Dense - 완전 연결 층을 만들기 위한 클래스 완전 연결 층 : 이전 층의 모든 연산을 받아들이는 층 import tensorflow as tf from tensorflow import keras 2-1) 생성할 때 파라미터 unit: 뉴런의 개수 activation: 활성화 함수로 기본값은 None 이고 sigmoid, softmax(다중 분류 문제에 사용), tanh(하이퍼볼릭 탄젠트 함수), relu 등을 설정할 수 있다. input_shape 는 입력 층(첫번째 층)의 경우 입력되는 데..
2024.03.20
no image
[딥러닝] Keras _ 패션 이미지 분류
Keras 의 내장 데이터 세트 - 종류 boston housing: 보스톤 집값으로 회귀에 사용 cifar10: 이미지 분류 데이터로 종류가 10가지 cifar100: 이미지 분류 데이터로 종류가 100가지 mnist: 손 글씨 데이터 fashion mnist: 의류 데이터로 클래스가 10가지 imdb: 영화 데이터로 분류에 활용 reuters: 뉴스 토픽 - 자연어 분류 - load_data 라는 함수를 호출하면 훈련 데이터 튜플 과 테스트 데이터 튜플로 데이터를 리턴 - 각각의 튜플은 피처 와 레이블로 나뉘어져 있음 1. 데이터 가져오기 fashion_mnist = keras.datasets.fashion_mnist (X_train_full, y_train_full), (X_test, y_test)..
2024.03.20

1. 데이터 가져오기

- 43개의 교통 표지판 과 관련된 4만여 개의 이미지로 만들어진 데이터 셋

- https://sid.erda.dk/public/archives/daaeac0d7ce1152aea9b61d9f1e19370/published-archive.html

 

Public Archive: daaeac0d7ce1152aea9b61d9f1e19370

Support ERDA User Guide Questions about ERDA? Please contact us at support@erda.dk

sid.erda.dk

- https://drive.google.com/drive/folders/1AktNCWqVBv-4xxme4OUC82OLJTHzMFsq?usp=sharing

 

 

- 데이터 다운로드

   colab
	!wget https://sid.erda.dk/public/archives/daaeac0d7ce1152aea9b61d9f1e19370/GTSRB_Final_Training_Images.zip
	!unzip -qq "GTSRB_Final_Training_Images.zip"
  • GTSRB\Final_Training\Images 디렉토리 안에 디렉토리 별로 이미지가 ppm 확장자로 분류되어 있다.

 

- ppm: 무손실 압축 이미지 파일 형식

 

- image 크기: 32 x 32

 

 

- 신경망 구조를 만들 때 선택 사항

  • 2차원 컨볼루션에서 필터 개수 와 커널 크기
  • Max Pool 에서의 커널 크기
  • Fully Connected 계층(Dense)에서의 유닛 개수
  • 계층 별 배치 크기, 최적화 알고리즘, 학습률, 할성화 함수, epoch 수 등

 

- 이미지 전처리

  • 이미지 크기를 32*32로 수정
  • 43개의 레이블을 생성: 디렉토리 이름이 레이블
  • 표지판의 경우 구분해야 할 것은 색상이 아니라 모양: 이미지를 흑백으로 변경
  • 데이터를 다룰 때 목적을 먼저 생각하고 작업을 시작해야 한다. (도메인)

 

- 필요한 패키지 와 상수 선언

  • 클래스 개수와 이미지의 크기를 변수에 저장
  • 이 두개의 절대로 변하지 않을 값
  • 변하지 않는 값을 장할 변수의 이름은 모두 대문자로 작성하는 것이 좋다.
    • SNAKE 표기법이라고 한다.
N_CLASSES = 43
RESIZED_IMAGE = (32, 32)

 

 

# 모듈

import matplotlib.pyplot as plt
#디렉토리를 핸들링 할 때 사용하는 모듈
import glob

from skimage.color import rgb2lab
from skimage.transform import resize
import numpy as np

from collections import namedtuple

from sklearn.model_selection import train_test_split

 

 

- 이미지 전처리를 위한 함수

  • 이름이 있는 튜플 생성 - 다른 언어에서는 튜플을 이런 형태로 만든다.
  • 튜플의 원래 목적은 변경할 수 없는 하나의 행(record, row)을 표현하기 위한 것
  • 인덱스가 아니라 이름으로 구별하는 것이 타당
Dataset = namedtuple('Dataset', ['X', 'y'])

 

 

# 포맷 변경해주는 함수

def to_tf_format(imgs):
    return np.stack([img[:, :, np.newaxis] for img in imgs], axis=0).astype(np.float32)

 

 

- 이미지 디렉토리에 있는 모든 이미지들에 라벨링을 하기 위한 작업
- 이미지의 크기 변경도 여기서 수행

def read_dataset_ppm(rootpath, n_labels, resize_to):
    #이미지 데이터 와 라벨을 저장할 list
    images = []
    labels = []
    
    for c in range(n_labels):
        #루트경로/00001/ 형태로 이미지 경로를 생성
        full_path = rootpath + "/" + format(c, '05d') + "/"
        #각 이미지 디렉토리를 순회하면서 확장자가 ppm 인 파일의 경로를 가지고
        for img_name in glob.glob(full_path + "*.ppm"):
            #이미지 읽어오기
            img = plt.imread(img_name).astype(np.float32)
            #이미지를 정규화
            img = rgb2lab(img / 255.0)[:, :,0]
            #이미지 크기를 조정
            if resize_to:
                img = resize(img, resize_to, mode='reflect')
            #라벨 생성
            #43개 짜리 배열을 만들어서 자신의 인덱스에 해당하는 값에만 1을 대입
            #원핫 인코딩 된 라벨
            label = np.zeros((n_labels, ), dtype=np.float32) 
            label[c] = 1.0
            
            images.append(img.astype(np.float32))
            labels.append(label)

    return Dataset(X = to_tf_format(images).astype(np.float32), y = np.matrix(labels).astype(np.float32))

 

 

# 이미지 전처리를 수행해서 데이터 만들기

dataset = read_dataset_ppm('./data/GTSRB/Final_Training/Images',
                          N_CLASSES,
                          RESIZED_IMAGE)
print(dataset.X.shape)
print(dataset.y.shape)
(39209, 32, 32, 1)
(39209, 43)

 

 

# 이미지 확인

plt.imshow(dataset.X[0, :, :, :].reshape(RESIZED_IMAGE))
print(dataset.y[0, :])
[[1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
  0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]]

 

 

 

- 훈련 데이터와 테스트 데이터 분리

#훈련 데이터 와 테스트 데이터 분리
idx_train, idx_test = train_test_split(range(dataset.X.shape[0]), test_size=0.25,
                                      random_state=42)

X_train = dataset.X[idx_train, :, :, :]
X_test = dataset.X[idx_test, :, :, :]

y_train = dataset.y[idx_train, :]
y_test = dataset.y[idx_test, :]

print(X_train.shape)
print(X_test.shape)
print(y_train.shape)
print(y_test.shape)
(29406, 32, 32, 1)
(9803, 32, 32, 1)
(29406, 43)
(9803, 43)

 

 

- 모델 생성

model = keras.Sequential()

#입력층 - Convolution 
model.add(keras.layers.Conv2D(32, kernel_size=3, activation='relu',
                             padding='same', input_shape=(32, 32, 1)))
model.add(keras.layers.MaxPooling2D(2))

model.add(keras.layers.Conv2D(64, kernel_size=3, activation='relu',
                             padding='same'))
model.add(keras.layers.MaxPooling2D(2))

model.add(keras.layers.Conv2D(128, kernel_size=3, activation='relu',
                             padding='same'))
model.add(keras.layers.MaxPooling2D(2))

model.add(keras.layers.Flatten())

model.add(keras.layers.Dense(100, activation='relu'))
model.add(keras.layers.Dropout(0.4))

model.add(keras.layers.Dense(100, activation='relu'))
model.add(keras.layers.Dropout(0.4))

model.add(keras.layers.Dense(100, activation='relu'))
model.add(keras.layers.Dropout(0.4))

model.add(keras.layers.Dense(43, activation='softmax'))

model.summary()

 

 

- 모델 컴파일

model.compile(optimizer='adam',
             loss='categorical_crossentropy',
             metrics=['accuracy'])

 

 

- 훈련

checkpoint_cb = keras.callbacks.ModelCheckpoint('best-cnn-models.keras')
early_stopping_cb = keras.callbacks.EarlyStopping(patience=2,
                                                 restore_best_weights=True)
history = model.fit(X_train, y_train, epochs=20,
                   callbacks = [checkpoint_cb, early_stopping_cb])

 

 

- 평가

print(model.evaluate(X_test, y_test))
307/307 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.8567 - loss: 0.3981
[0.39652204513549805, 0.853820264339447]

 

!git clone https://github.com/MachineLearning2020/Homework2-Caltech101.git

 

 

 

 

 

 

 

1. 데이터 가져오기

from tensorflow import keras
from sklearn.model_selection import train_test_split

#데이터 읽어오기
(train_input, train_target), (test_input, test_target) = keras.datasets.fashion_mnist.load_data()
rint(train_input.shape)

 

 

 

 

2. 스케일링 및 차원 수정

# 흑백 이미지라서 차원을 1개 늘려서 255 로 나눔
train_scaled = train_input.reshape(-1, 28, 28, 1) / 255.0
print(train_scaled.shape) 

train_scaled, val_scaled, train_target, val_target = train_test_split(train_scaled, train_target, test_size=0.2, random_state=42)
(60000, 28, 28, 1)

 

 

 

 

3. 모델 생성

#모델 만들기
model = keras.Sequential()

#입력 층을 합성곱 층을 사용
model.add(keras.layers.Conv2D(32, kernel_size=3, activation="relu",
                             padding='same', input_shape=(28, 28, 1)))
model.add(keras.layers.MaxPooling2D(2))

model.add(keras.layers.Conv2D(64, kernel_size=3, activation="relu",
                             padding='same'))
model.add(keras.layers.MaxPooling2D(2))

#데이터를 1차원으로 만들어주는 층
model.add(keras.layers.Flatten())

#Dense 는 1차원의 데이터만 사용
model.add(keras.layers.Dense(100, activation="relu"))

#드랍 아웃 적용
model.add(keras.layers.Dropout(0.4))

model.add(keras.layers.Dense(10, activation="softmax"))

 

# 모델 확인

model.summary()

 

 

 

 

 

4. 모델 컴파일 (훈련)

model.compile(optimizer="adam", loss="sparse_categorical_crossentropy",
             metrics=["accuracy"])

#체크 포인트
checkpoint_cb = keras.callbacks.ModelCheckpoint("best-cnn-model.keras")

#2번의 epoch 동안 점수가 좋아지지 않으면 조기 종료
early_stopping_cb = keras.callbacks.EarlyStopping(patience=2,
                                                 restore_best_weights=True)
history = model.fit(train_scaled, train_target, epochs=20,
                   validation_data=(val_scaled, val_target),
                   callbacks=[checkpoint_cb, early_stopping_cb])

 

 

 

 

 

4. 모델 평가

model.evaluate(val_scaled, val_target)
  • 아래 합성곱 층으로 내려갈 때 뉴런의 개수를 늘렸는데 중간에 MaxPooling2D(Dropout을 적용해도 동일) 을 적용했기 때문에 실제 파라미터의 개수가 줄어들어 사용되므로 늘린 티가 나지 않음
#클래스 이름
classes = ['티셔츠', '바지', '스웨터', '드레스', '코트',
          '샌달', '셔츠', '스니커즈', '가방', '앵클 부츠']


preds = model.predict(val_scaled[0:1])
print(preds)
print(classes[np.argmax(preds)])

plt.imshow(val_scaled[0].reshape(28, 28), cmap='gray_r')
plt.show()

 

 

# 훈련에 사용한 데이터가 스케일되어 있으므로, 예측 데이터도 스케일링

test_scaled = test_input.reshape(-1, 28, 28, 1)/255.0
model.evaluate(test_scaled, test_target)

 

 

# 모델 저장

model.save("best-cnn-models.keras")
  • 이전 API에서는 확장자를 h5를 권장했는데 최근에는 keras를 권장

 

 

 

- 국가 통계 포털에서 인구 정보 내려받기

http://kosis.kr/statHtml/statHtml.do?orgId=101&tblId=DT_1IN1509&

 

KOSIS

 

kosis.kr

 

기본코드

import pandas as pd
import numpy as np
import platform
import matplotlib.pyplot as plt

%matplotlib inline
path = "c:/Windows/Fonts/malgun.ttf"
from matplotlib import font_manager, rc
if platform.system() == 'Darwin':
    rc('font', family='AppleGothic')
elif platform.system() == 'Windows':
    font_name = font_manager.FontProperties(fname=path).get_name()
    rc('font', family=font_name)
else:
    print('알 수 없는 시스템')    
plt.rcParams['axes.unicode_minus'] = False

 

 

 

 

1. 데이터 읽어오기

population = pd.read_excel('./data/population.xlsx', header=1)
population

 

 

 

 

2. 데이터 전처리

 

#결측치를 앞의 데이터로 채우기

population.fillna(method='ffill', inplace=True)

 

 

#컬럼이름 변경하기

population.rename(columns = {'행정구역별(시군구)(1)':'광역시도', 
                             '행정구역별(시군구)(2)':'시도', 
                             '합계':'인구수'}, inplace=True)
population.head()

 

 

# 소계를 제외한 데이터만 가져오기

population = population[(population['시도'] != '소계')]
population.head()

 

 

# 컬럼 이름 변경

population.is_copy = False
population.rename(columns = {'성별(1)':'구분'}, inplace=True)
population.loc[population['구분'] == '계', '구분'] = '합계'
population.head()

 

 

# 청년과 노년 구분

population['20-39세'] = population['20~24세'] + population['25~29세'] + \
                        population['30~34세'] + population['35~39세']
    
population['65세이상'] = population['65~69세'] + population['70~74세'] + \
                        population['75~79세'] + population['80~84세'] + \
                        population['85세이상']

population.head(5)

 

 

# 피벗 테이블 생성

pop = pd.pivot_table(population, 
                     index = ['광역시도', '시도'], 
                     columns = ['구분'],
                     values = ['인구수', '20-39세', '65세이상'])
pop.head()

 

 

# 새로운 컬럼 추가 - 소멸 비율

pop['소멸비율'] = pop['20-39세','합계'] / (pop['65세이상','합계'] / 2)
pop.head()

 

 

# 새로운 컬럼 추가 - 소멸 위기 지역

pop['소멸위기지역'] = pop['소멸비율'] < 1.0
pop.head()
print(pop[pop['소멸위기지역']==True].index.get_level_values(1))
Index(['영월군', '남해군', '산청군', '의령군', '하동군', '함양군', '합천군', '군위군', '면부', '봉화군',
       '영덕군', '영양군', '의성군', '청도군', '청송군', '강화군', '면부', '강진군', '고흥군', '곡성군',
       '구례군', '면부', '보성군', '신안군', '완도군', '장흥군', '진도군', '함평군', '해남군', '고창군',
       '면부', '무주군', '부안군', '순창군', '임실군', '장수군', '진안군', '부여군', '서천군', '청양군',
       '괴산군', '단양군', '보은군'],
      dtype='object', name='시도')

 

 

# 인덱스 초기화

pop.reset_index(inplace=True) 
pop.head()

 

 

 

 

 

3. 시도 이름 합치기

# 새로운 컬럼 추가 - 첫번째와 두번째 컬럼 이름 합치기

tmp_coloumns = [pop.columns.get_level_values(0)[n] + \
                pop.columns.get_level_values(1)[n] 
                for n in range(0,len(pop.columns.get_level_values(0)))]

pop.columns = tmp_coloumns
pop.head()

 

 

# 데이터 확인

pop.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 303 entries, 0 to 302
Data columns (total 13 columns):
 #   Column    Non-Null Count  Dtype  
---  ------    --------------  -----  
 0   광역시도      303 non-null    object 
 1   시도        303 non-null    object 
 2   20-39세남자  303 non-null    int64  
 3   20-39세여자  303 non-null    int64  
 4   20-39세합계  303 non-null    int64  
 5   65세이상남자   303 non-null    int64  
 6   65세이상여자   303 non-null    int64  
 7   65세이상합계   303 non-null    int64  
 8   인구수남자     303 non-null    int64  
 9   인구수여자     303 non-null    int64  
 10  인구수합계     303 non-null    int64  
 11  소멸비율      303 non-null    float64
 12  소멸위기지역    303 non-null    bool   
dtypes: bool(1), float64(1), int64(9), object(2)
memory usage: 28.8+ KB

 

 

# 시도 고유값 확인

print(pop['시도'].unique())
['강릉시' '고성군' '동부' '동해시' '면부' '삼척시' '속초시' '양구군' '양양군' '영월군' '원주시' '읍부'
 '인제군' '정선군' '철원군' '춘천시' '태백시' '평창군' '홍천군' '화천군' '횡성군' '가평군' '고양시' '과천시'
 '광명시' '광주시' '구리시' '군포시' '권선구' '기흥구' '김포시' '남양주시' '단원구' '덕양구' '동두천시' '동안구'
 '만안구' '부천시' '분당구' '상록구' '성남시' '수원시' '수정구' '수지구' '시흥시' '안산시' '안성시' '안양시'
 '양주시' '양평군' '여주시' '연천군' '영통구' '오산시' '용인시' '의왕시' '의정부시' '이천시' '일산동구'
 '일산서구' '장안구' '중원구' '처인구' '파주시' '팔달구' '평택시' '포천시' '하남시' '화성시' '거제시' '거창군'
 '김해시' '남해군' '마산합포구' '마산회원구' '밀양시' '사천시' '산청군' '성산구' '양산시' '의령군' '의창구'
 '진주시' '진해구' '창녕군' '창원시' '통영시' '하동군' '함안군' '함양군' '합천군' '경산시' '경주시' '고령군'
 '구미시' '군위군' '김천시' '남구' '문경시' '봉화군' '북구' '상주시' '성주군' '안동시' '영덕군' '영양군'
 '영주시' '영천시' '예천군' '울릉군' '울진군' '의성군' '청도군' '청송군' '칠곡군' '포항시' '광산구' '동구'
 '서구' '달서구' '달성군' '수성구' '중구' '대덕구' '유성구' '강서구' '금정구' '기장군' '동래구' '부산진구'
 '사상구' '사하구' '수영구' '연제구' '영도구' '해운대구' '강남구' '강동구' '강북구' '관악구' '광진구' '구로구'
 '금천구' '노원구' '도봉구' '동대문구' '동작구' '마포구' '서대문구' '서초구' '성동구' '성북구' '송파구' '양천구'
 '영등포구' '용산구' '은평구' '종로구' '중랑구' '세종시' '울주군' '강화군' '계양구' '남동구' '미추홀구' '부평구'
 '연수구' '옹진군' '강진군' '고흥군' '곡성군' '광양시' '구례군' '나주시' '담양군' '목포시' '무안군' '보성군'
 '순천시' '신안군' '여수시' '영광군' '영암군' '완도군' '장성군' '장흥군' '진도군' '함평군' '해남군' '화순군'
 '고창군' '군산시' '김제시' '남원시' '덕진구' '무주군' '부안군' '순창군' '완산구' '완주군' '익산시' '임실군'
 '장수군' '전주시' '정읍시' '진안군' '서귀포시' '제주시' '계룡시' '공주시' '금산군' '논산시' '당진시' '동남구'
 '보령시' '부여군' '서북구' '서산시' '서천군' '아산시' '예산군' '천안시' '청양군' '태안군' '홍성군' '괴산군'
 '단양군' '보은군' '상당구' '서원구' '영동군' '옥천군' '음성군' '제천시' '증평군' '진천군' '청원구' '청주시'
 '충주시' '흥덕구']

 

 

# 시도 고유 이름

si_name = [None] * len(pop)

#광역시가 아닌 곳 중에서 구를 가지고 있는 시도들의 구이름 디셔너리 생성
tmp_gu_dict = {'수원':['장안구', '권선구', '팔달구', '영통구'], 
                       '성남':['수정구', '중원구', '분당구'], 
                       '안양':['만안구', '동안구'], 
                       '안산':['상록구', '단원구'], 
                       '고양':['덕양구', '일산동구', '일산서구'], 
                       '용인':['처인구', '기흥구', '수지구'], 
                       '청주':['상당구', '서원구', '흥덕구', '청원구'], 
                       '천안':['동남구', '서북구'], 
                       '전주':['완산구', '덕진구'], 
                       '포항':['남구', '북구'], 
                       '창원':['의창구', '성산구', '진해구', '마산합포구', '마산회원구'], 
                       '부천':['오정구', '원미구', '소사구']}

for n in pop.index:
    #고성이 2곳이므로 도를 추가
    if pop['광역시도'][n][-3:] not in ['광역시', '특별시', '자치시']:
        if pop['시도'][n][:-1]=='고성' and pop['광역시도'][n]=='강원도':
            si_name[n] = '고성(강원)'
        elif pop['시도'][n][:-1]=='고성' and pop['광역시도'][n]=='경상남도':
            si_name[n] = '고성(경남)'
        #그 이외의 지역은 마지막 한글자를 제거해서 군 이나 시 글자를 제거
        else:
             si_name[n] = pop['시도'][n][:-1]
        for keys, values in tmp_gu_dict.items():
            if pop['시도'][n] in values:
                if len(pop['시도'][n])==2:
                    si_name[n] = keys + ' ' + pop['시도'][n]
                elif pop['시도'][n] in ['마산합포구','마산회원구']:
                    si_name[n] = keys + ' ' + pop['시도'][n][2:-1]
                else:
                    si_name[n] = keys + ' ' + pop['시도'][n][:-1]
                        #세종은 이름을 수정    
    elif pop['광역시도'][n] == '세종특별자치시':
        si_name[n] = '세종'
    else:
        if len(pop['시도'][n])==2:
            si_name[n] = pop['광역시도'][n][:2] + ' ' + pop['시도'][n]
        else:
            si_name[n] = pop['광역시도'][n][:2] + ' ' + pop['시도'][n][:-1]
print(si_name)
['강릉', '고성(강원)', '동', '동해', '면', '삼척', '속초', '양구', '양양', '영월', '원주', '읍', '인제', '정선', '철원', '춘천', '태백', '평창', '홍천', '화천', '횡성', '가평', '고양', '과천', '광명', '광주', '구리', '군포', '수원 권선', '용인 기흥', '김포', '남양주', '안산 단원', '고양 덕양', '동두천', '동', '안양 동안', '안양 만안', '면', '부천', '성남 분당', '안산 상록', '성남', '수원', '성남 수정', '용인 수지', '시흥', '안산', '안성', '안양', '양주', '양평', '여주', '연천', '수원 영통', '오산', '용인', '읍', '의왕', '의정부', '이천', '고양 일산동', '고양 일산서', '수원 장안', '성남 중원', '용인 처인', '파주', '수원 팔달', '평택', '포천', '하남', '화성', '거제', '거창', '고성(경남)', '김해', '남해', '동', '창원 합포', '창원 회원', '면', '밀양', '사천', '산청', '창원 성산', '양산', '읍', '의령', '창원 의창', '진주', '창원 진해', '창녕', '창원', '통영', '하동', '함안', '함양', '합천', '경산', '경주', '고령', '구미', '군위', '김천', '포항 남구', '동', '면', '문경', '봉화', '포항 북구', '상주', '성주', '안동', '영덕', '영양', '영주', '영천', '예천', '울릉', '울진', '읍', '의성', '청도', '청송', '칠곡', '포항', '광주 광산', '광주 남구', '광주 동구', '광주 북구', '광주 서구', '대구 남구', '대구 달서', '대구 달성', '대구 동구', '대구 동부', '대구 면부', '대구 북구', '대구 서구', '대구 수성', '대구 읍부', '대구 중구', '대전 대덕', '대전 동구', '대전 서구', '대전 유성', '대전 중구', '부산 강서', '부산 금정', '부산 기장', '부산 남구', '부산 동구', '부산 동래', '부산 동부', '부산 면부', '부산 부산진', '부산 북구', '부산 사상', '부산 사하', '부산 서구', '부산 수영', '부산 연제', '부산 영도', '부산 읍부', '부산 중구', '부산 해운대', '서울 강남', '서울 강동', '서울 강북', '서울 강서', '서울 관악', '서울 광진', '서울 구로', '서울 금천', '서울 노원', '서울 도봉', '서울 동대문', '서울 동작', '서울 마포', '서울 서대문', '서울 서초', '서울 성동', '서울 성북', '서울 송파', '서울 양천', '서울 영등포', '서울 용산', '서울 은평', '서울 종로', '서울 중구', '서울 중랑', '세종', '세종', '세종', '세종', '울산 남구', '울산 동구', '울산 동부', '울산 면부', '울산 북구', '울산 울주', '울산 읍부', '울산 중구', '인천 강화', '인천 계양', '인천 남동', '인천 동구', '인천 동부', '인천 면부', '인천 미추홀', '인천 부평', '인천 서구', '인천 연수', '인천 옹진', '인천 읍부', '인천 중구', '강진', '고흥', '곡성', '광양', '구례', '나주', '담양', '동', '면', '목포', '무안', '보성', '순천', '신안', '여수', '영광', '영암', '완도', '읍', '장성', '장흥', '진도', '함평', '해남', '화순', '고창', '군산', '김제', '남원', '전주 덕진', '동', '면', '무주', '부안', '순창', '전주 완산', '완주', '읍', '익산', '임실', '장수', '전주', '정읍', '진안', '동', '면', '서귀포', '읍', '제주', '계룡', '공주', '금산', '논산', '당진', '천안 동남', '동', '면', '보령', '부여', '천안 서북', '서산', '서천', '아산', '예산', '읍', '천안', '청양', '태안', '홍성', '괴산', '단양', '동', '면', '보은', '청주 상당', '청주 서원', '영동', '옥천', '음성', '읍', '제천', '증평', '진천', '청주 청원', '청주', '충주', '청주 흥덕']

 

 

#도시 이름을 DataFrame에 추가하고 불필요한 데이터 삭제

pop['ID'] = si_name

del pop['20-39세남자']
del pop['65세이상남자']
del pop['65세이상여자']

pop.head()

 

 

 

 

3. Cartogram을 위한 엑셀 파일 

 

 

# 엑셀 파일 읽기

draw_korea_raw = pd.read_excel('./data/draw_korea_raw.xlsx')
draw_korea_raw

 

 

 

 

 

 

4. stack으로 묶기

# 컬럼 이름을 인덱스로 만들기

draw_korea_raw_stacked = pd.DataFrame(draw_korea_raw.stack())
draw_korea_raw_stacked

 

 

# 인덱스 초기화

draw_korea_raw_stacked.reset_index(inplace=True)
draw_korea_raw_stacked

 

 

# 컬럼 이름 변경

draw_korea_raw_stacked.rename(columns={'level_0':'y', 'level_1':'x', 0:'ID'}, 
                              inplace=True)
draw_korea_raw_stacked

 

 

 

 

 

5. cartogram의 기본이 되는 지도 그리기

BORDER_LINES = [
    [(5, 1), (5,2), (7,2), (7,3), (11,3), (11,0)], # 인천
    [(5,4), (5,5), (2,5), (2,7), (4,7), (4,9), (7,9), 
     (7,7), (9,7), (9,5), (10,5), (10,4), (5,4)], # 서울
    [(1,7), (1,8), (3,8), (3,10), (10,10), (10,7), 
     (12,7), (12,6), (11,6), (11,5), (12, 5), (12,4), 
     (11,4), (11,3)], # 경기도
    [(8,10), (8,11), (6,11), (6,12)], # 강원도
    [(12,5), (13,5), (13,4), (14,4), (14,5), (15,5), 
     (15,4), (16,4), (16,2)], # 충청북도
    [(16,4), (17,4), (17,5), (16,5), (16,6), (19,6), 
     (19,5), (20,5), (20,4), (21,4), (21,3), (19,3), (19,1)], # 전라북도
    [(13,5), (13,6), (16,6)], # 대전시
    [(13,5), (14,5)], #세종시
    [(21,2), (21,3), (22,3), (22,4), (24,4), (24,2), (21,2)], #광주
    [(20,5), (21,5), (21,6), (23,6)], #전라남도
    [(10,8), (12,8), (12,9), (14,9), (14,8), (16,8), (16,6)], #충청북도
    [(14,9), (14,11), (14,12), (13,12), (13,13)], #경상북도
    [(15,8), (17,8), (17,10), (16,10), (16,11), (14,11)], #대구
    [(17,9), (18,9), (18,8), (19,8), (19,9), (20,9), (20,10), (21,10)], #부산
    [(16,11), (16,13)], #울산
#     [(9,14), (9,15)], 
    [(27,5), (27,6), (25,6)],
]

 

 

# 지역 이름 표시

plt.figure(figsize=(8, 11))

# 지역 이름 표시
for idx, row in draw_korea_raw_stacked.iterrows():
    
    # 광역시는 구 이름이 겹치는 경우가 많아서 시단위 이름도 같이 표시
    # (중구, 서구)
    if len(row['ID'].split())==2:
        dispname = '{}\n{}'.format(row['ID'].split()[0], row['ID'].split()[1])
    elif row['ID'][:2]=='고성':
        dispname = '고성'
    else:
        dispname = row['ID']

 

 

# 시도 경계 그리기

for path in BORDER_LINES:
    ys, xs = zip(*path)
    plt.plot(xs, ys, c='black', lw=1.5)

#y축의 위아래 변경
plt.gca().invert_yaxis()
#plt.gca().set_aspect(1)

#축과 라벨 제거
plt.axis('off')

#자동 레이아웃 설정
plt.tight_layout()
plt.show()

 

 

 

# draw_korea_raw_stacked와 pop의 도시이름 일치시키기

#draw_korea_raw_stacked 와 pop 의 도시이름 비교
print(set(draw_korea_raw_stacked['ID'].unique()) - set(pop['ID'].unique()))
print(set(pop['ID'].unique()) - set(draw_korea_raw_stacked['ID'].unique()))

#일치하지 않는 데이터 삭제
tmp_list = list(set(pop['ID'].unique()) - set(draw_korea_raw_stacked['ID'].unique()))

for tmp in tmp_list:
    pop = pop.drop(pop[pop['ID']==tmp].index)
                       
print(set(pop['ID'].unique()) - set(draw_korea_raw_stacked['ID'].unique()))
{'부천 원미', '부천 오정', '부천 소사', '인천 남구'}
{'포항', '부산 면부', '대구 읍부', '성남', '용인', '인천 면부', '대구 동부', '인천 미추홀', '인천 동부', '안산', '부산 동부', '수원', '창원', '울산 면부', '읍', '인천 읍부', '부산 읍부', '고양', '천안', '동', '청주', '울산 읍부', '대구 면부', '안양', '전주', '면', '울산 동부', '부천'}
set()
pop = pd.merge(pop, draw_korea_raw_stacked, how='left', on=['ID'])
pop.head()

 

 

# 좌표와 인구수 이용해서 피벗 테이블 생성

mapdata = pop.pivot_table(index='y', columns='x', values='인구수합계')
mapdata

 

 

# Nan이 아닌 데이터 확인




 

 

 

 

 

 

6. Cartogram 그리기

# DataFrame과 컬럼 이름 및 색상 값을 받아서 Cartogram을 그려주는 함수

def drawKorea(targetData, blockedMap, cmapname):
    gamma = 0.75

    #인구수 데이터의 크고 낮음을 분류하기 위한 값 만들기
    whitelabelmin = (max(blockedMap[targetData]) - 
                                     min(blockedMap[targetData]))*0.25 + \
                                                                min(blockedMap[targetData])
    #컬럼이름을 대입하기
    datalabel = targetData
    #최대값과 최소값 구하기
    vmin = min(blockedMap[targetData])
    vmax = max(blockedMap[targetData])
    
    #x 와 y를 가지고 피봇 테이블 만들기
    mapdata = blockedMap.pivot_table(index='y', columns='x', values=targetData)
    #데이터가 존재하는 것 골라내기
    masked_mapdata = np.ma.masked_where(np.isnan(mapdata), mapdata)
    #그래프 영역 크기 만들기
    plt.figure(figsize=(9, 11))
    #색상 설정
    plt.pcolor(masked_mapdata, vmin=vmin, vmax=vmax, cmap=cmapname, 
               edgecolor='#aaaaaa', linewidth=0.5)
    
    # 지역 이름 표시
    for idx, row in blockedMap.iterrows():
        # 광역시는 구 이름이 겹치는 경우가 많아서 시단위 이름도 같이 표시 
        #(중구, 서구)
        if len(row['ID'].split())==2:
            dispname = '{}\n{}'.format(row['ID'].split()[0], row['ID'].split()[1])
        elif row['ID'][:2]=='고성':
            dispname = '고성'
        else:
            dispname = row['ID']
        # 서대문구, 서귀포시 같이 이름이 3자 이상인 경우에 작은 글자로 표시
        if len(dispname.splitlines()[-1]) >= 3:
            fontsize, linespacing = 10.0, 1.1
        else:
            fontsize, linespacing = 11, 1.
            
        #글자색상 만들기
        annocolor = 'white' if row[targetData] > whitelabelmin else 'black'
        #텍스트 출력하기
        plt.annotate(dispname, (row['x']+0.5, row['y']+0.5), weight='bold',
                     fontsize=fontsize, ha='center', va='center', color=annocolor,
                     linespacing=linespacing)
    
    # 시도 경계 그리기
    for path in BORDER_LINES:
        ys, xs = zip(*path)
        plt.plot(xs, ys, c='black', lw=2)

    plt.gca().invert_yaxis()

    plt.axis('off')

    cb = plt.colorbar(shrink=.1, aspect=10)
    cb.set_label(datalabel)

    plt.tight_layout()
    plt.show()

 

 

 

# 인구수 합계로 Catrogram 그리기

drawKorea('인구수합계', pop, 'Blues')

 

 

#소멸위기지역으로 Cartogram 그리기

pop['소멸위기지역'] = [1 if con else 0 for con in pop['소멸위기지역']]
drawKorea('소멸위기지역', pop, 'Reds')

 

 

# 여성 비율로 Catrogram 그리기

pop['여성비'] = (pop['인구수여자']/pop['인구수합계'])*100
drawKorea('여성비', pop, 'RdBu')

 

 

# 청년 층의 여성 비율을 이용한 Cartogram 그리기

pop['2030여성비'] = (pop['20-39세여자']/pop['20-39세합계'])*100
drawKorea('2030여성비', pop, 'RdBu')

 

 

# 지역 이름을 index로 설정

import folium
import json
import warnings

warnings.simplefilter(action='ignore', category=FutureWarning)
pop_folium = pop.set_index('ID')
pop_folium.head()

 

 

#인구 수 합계를 이용한 단계구분도

#한국 지도 시도 경계 좌표를 가진 파일을 가져오기
geo_path = './data/korea_geo_simple.json'
geo_str = json.load(open(geo_path, encoding='utf-8'))

map = folium.Map(location=[36.2002, 127.054], zoom_start=7)
folium.Choropleth(geo_data = geo_str,
               data = pop_folium['인구수합계'],
               columns = [pop_folium.index, pop_folium['인구수합계']],
               fill_color = 'YlGnBu',
               key_on = 'feature.id').add_to(map)

map
map = folium.Map(location=[36.2002, 127.054], zoom_start=7)
folium.Choropleth(geo_data = geo_str,
               data = pop_folium['소멸위기지역'],
               columns = [pop_folium.index, pop_folium['소멸위기지역']],
               fill_color = 'YlOrRd',
               key_on = 'feature.id').add_to(map)

map

 

 

 

 

 

 

 

 

 

- https://data.seoul.go.kr/

- 서울시 5대 점죄 발생현황 통계 데이터

- crime.txt

 

 

 

1. 데이터 가져오기

import pandas as pd
import folium
import json

criminal = pd.read_csv('./data/crime.txt', sep='\t', thousands=',', encoding='utf-8')

 

 

 

 

 

2. 데이터 전처리

 

# 불필요한 컬럼 제거

criminal.drop(['기간', '합계검거', '살인검거', '강도검거', '강간검거', '절도검거', '폭력검거'], axis=1, inplace=True)
criminal.drop([0], inplace=True)

 

 

# 컬럼 이름 설정

criminal.rename(columns={'살인발생':'살인', '강도발생':'강도', '강간발생':'강간', '절도발생':'절도', '폭력발생':'폭력'}, inplace=True)

 

 

# 인덱스 설정

criminal.set_index('자치구', inplace=True)

 

 

 

 

3. 단계 구분도

geo_path = './data/seoul.json'
geo_str = json.load(open(geo_path, encoding='utf-8'))
map = folium.Map(location=[37.5502, 126.982], zoom_start=11,
                 tiles='Stamen Toner')
folium.Choropleth(geo_data = geo_str,
               data = criminal['살인'],
               columns = [criminal.index, criminal['살인']],
               fill_color = 'YlGnBu',
               key_on = 'feature.id').add_to(map)
map

 

 

 

 

 

4. 주소와 위도 경도 가져오기

# 경찰서 이름 만들기

station_name = []
for name in criminal.index:
    station_name.append('서울' + str(name[:-1]) + '경찰서')

print(station_name)
['서울종로경찰서', '서울중경찰서', '서울용산경찰서', '서울성동경찰서', '서울광진경찰서', '서울동대문경찰서', '서울중랑경찰서', '서울성북경찰서', '서울강북경찰서', '서울도봉경찰서', '서울노원경찰서', '서울은평경찰서', '서울서대문경찰서', '서울마포경찰서', '서울양천경찰서', '서울강서경찰서', '서울구로경찰서', '서울금천경찰서', '서울영등포경찰서', '서울동작경찰서', '서울관악경찰서', '서울서초경찰서', '서울강남경찰서', '서울송파경찰서', '서울강동경찰서']

 

 

# 카카오 지오코딩 API를 이용해서 경찰서 주소와 위도, 경도 찾아오기

import requests
import json
from urllib.parse import quote

#경찰서 주소와 위도, 경도 찾아오기
station_address = []
station_lat = []
station_lng = []
for name in station_name:
    q = quote(name)
    url = 'https://dapi.kakao.com/v2/local/search/keyword.json?query=' + q
    headers = {'Authorization': 'KakaoAK {}'.format('c454c0e64688ce2bde2dfff9cceced87')}
    data = requests.post(url, headers=headers)
    result = json.loads(data.text) #파싱한 결과
    documents = result["documents"]
    
    address = documents[0]["address_name"]
    x = documents[0]["x"]
    y = documents[0]["y"]
    
    station_address.append(address)
    station_lat.append(y)
    station_lng.append(x)
print(station_address)
print(station_lat)
print(station_lng)
['서울 종로구 공평동 1', '서울 강남구 역삼동 830-23', '서울 용산구 원효로1가 12-12', '서울 성동구 행당동 192-8', '서울 광진구 구의동 254-32', '서울 동대문구 청량리동 229', '서울 중랑구 신내동 810', '서울 성북구 삼선동5가 301', '서울 강북구 번동 415-15', '서울 도봉구 창동 17', '서울 노원구 하계동 250', '서울 은평구 불광동 산 24', '서울 서대문구 미근동 165', '서울 마포구 아현동 618-1', '서울 양천구 신정동 321', '서울 강서구 화곡동 980-27', '서울 구로구 구로동 3-25', '서울 금천구 시흥동 1030', '서울 영등포구 당산동3가 2-11', '서울 동작구 노량진동 72-35', '서울 관악구 봉천동 1695-5', '서울 서초구 서초동 1726-1', '서울 강남구 대치동 998', '서울 송파구 가락동 9', '서울 강동구 성내동 541-1']

['37.57185082446809', '37.49388566996322', '37.54117933635974', '37.5617947420747', '37.54292164557882', '37.5850804205685', '37.6182390801576', '37.58989767143614', '37.637406561393085', '37.65339041848567', '37.6423345266253', '37.6285242467996', '37.5649023404956', '37.550824477077796', '37.5165549067037', '37.5513470867979', '37.5070387936593', '37.45725314643252', '37.52585220719', '37.5131469479959', '37.4745875338795', '37.49561086789221', '37.5093899463715', '37.5019757297179', '37.52867274661258']

['126.98417036713045', '127.03221660594751', '126.96777437089538', '127.036472076258', '127.08396170505674', '127.045762797538', '127.10454224897', '127.01625465308534', '127.02733870040592', '127.05270598499145', '127.071404832413', '126.928631038584', '126.966790740735', '126.95400918908302', '126.865542970902', '126.849886071842', '126.890224925815', '126.89674921449215', '126.90094457988', '126.942857105642', '126.951062346192', '127.00528097622329', '127.067070541137', '127.12716129352492', '127.12688982770433']

 

 

 

 

 

 

5. 지도 출력

criminal['lat'] = station_lat
criminal['lng'] = station_lng
map = folium.Map(location=[37.5502, 126.982], zoom_start=11)

for n in criminal.index:
    folium.Marker([criminal['lat'][n], 
                   criminal['lng'][n]]).add_to(map)
map

 

map = folium.Map(location=[37.5502, 126.982], zoom_start=11)

for n in criminal.index:
    folium.CircleMarker([criminal['lat'][n], criminal['lng'][n]],
                        radius = criminal['폭력'][n]/100.0, 
                        color='#3186cc', fill_color='#3186cc', fill=True).add_to(map)
    
map
map = folium.Map(location=[37.5502, 126.982], zoom_start=11)

folium.Choropleth(geo_data = geo_str,
               data = criminal['살인'],
               columns = [criminal.index, criminal['살인']],
               fill_color = 'YlGnBu',
               key_on = 'feature.id').add_to(map)

for n in criminal.index:
    folium.CircleMarker([criminal['lat'][n], criminal['lng'][n]],
                        radius = criminal['폭력'][n]/100, 
                        color='#3186cc', fill_color='#3186cc', fill=True).add_to(map)
    
map

https://github.com/southkorea/southkorea-maps

한국 지리정보 사이트

 

 

 

1. 데이터 읽어오기

state_unemployment = 'data/US_Unemployment_Oct2012.csv'
state_data = pd.read_csv(state_unemployment)
print(state_data.head())
  State  Unemployment
0    AL           7.1
1    AK           6.8
2    AZ           8.1
3    AR           7.2
4    CA          10.1

 

 

 

 

2. 단계 구분도 출력

  • folium.Map으로 만든 지도에 choropleth 함수 호출
    • geo_data 옵션에 아이디와 위도 및 경도를 가진 json 파일의 경로를 설정해주고 data에 출력한 DF 설정
    • key_on에 json 파일에서 사용되는 id 컬럼 지정
state_geo = 'data/folium_us-states.json'

map = folium.Map(location=[40, -98], zoom_start=4)

folium.Choropleth(geo_data=state_geo, data=state_data,
             columns=['State', 'Unemployment'],
             key_on='feature.id',
             fill_color='YlGn',
             legend_name='Unemployment Rate (%)').add_to(map)
map

 

 

 

 

 

 

1. 서울시 자치구별 CCTV 현황 데이터 가져오기

- http://data.seoul.go.kr/

 

열린데이터광장 메인

데이터분류,데이터검색,데이터활용

data.seoul.go.kr

- CCTV 검색

- xlsx 파일을 다운로드(파일 이름을 cctv로 수정)

 

 

# 데이터 읽기

import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
from pandas import Series, DataFrame
import platform
from matplotlib import font_manager, rc

#데이터 읽어오기
cctv = pd.read_excel('./data/cctv.xlsx')
print(cctv.head())
cctv.info()


<class 'pandas.core.frame.DataFrame'>
RangeIndex: 25 entries, 0 to 24
Data columns (total 10 columns):
 #   Column    Non-Null Count  Dtype  
---  ------    --------------  -----  
 0   기관명       25 non-null     object 
 1   소계        25 non-null     int64  
 2   2011년 이전  23 non-null     float64
 3   2012년     23 non-null     float64
 4   2013년     23 non-null     float64
 5   2014년     25 non-null     int64  
 6   2015년     25 non-null     int64  
 7   2016년     25 non-null     int64  
 8   2017년     25 non-null     int64  
 9   2018년     25 non-null     int64  
dtypes: float64(3), int64(6), object(1)
memory usage: 2.1+ KB

 

 

 

 

 

2. 서울시 자치구별 인구 현황 데이터 가져오기

- http://data.seoul.go.kr/

- 자치구별 인구 검색

- pop.txt

 

 

# 데이터 읽기

pop = pd.read_csv('./data/pop.txt',  encoding='utf-8', skiprows=2, delimiter='\t', thousands=',')
print(pop.head())
print()
pop.info()



<class 'pandas.core.frame.DataFrame'>
RangeIndex: 26 entries, 0 to 25
Data columns (total 14 columns):
 #   Column    Non-Null Count  Dtype  
---  ------    --------------  -----  
 0   기간        26 non-null     object 
 1   자치구       26 non-null     object 
 2   세대        26 non-null     int64  
 3   계         26 non-null     int64  
 4   남자        26 non-null     int64  
 5   여자        26 non-null     int64  
 6   계.1       26 non-null     int64  
 7   남자.1      26 non-null     int64  
 8   여자.1      26 non-null     int64  
 9   계.2       26 non-null     int64  
 10  남자.2      26 non-null     int64  
 11  여자.2      26 non-null     int64  
 12  세대당인구     26 non-null     float64
 13  65세이상고령자  26 non-null     int64  
dtypes: float64(1), int64(11), object(2)
memory usage: 3.0+ KB

 

 

 

 

 

3. 데이터 전처리

# 컬럼 이름 변경

cctv.rename(columns={cctv.columns[0] : '구별'}, inplace=True)
print(cctv.head())
print()

gu = []
for x in cctv['구별']:
    gu.append(x.replace(' ', ''))
cctv['구별'] = gu

pop.rename(columns={pop.columns[1] : '구별'}, inplace=True)
print(pop.head())




 

 

# 필터링

#pop에서 컬럼 추출
pop = pop[['기간', '구별', '계', '남자', '여자']]

#pop의 첫번째 행은 합계
#첫번째 행 제거
pop.drop([0], inplace=True)

#여성인구 비율을 알아보기 위해서 새로운 열 생성
pop['여성비율'] = pop['여자']/pop['계']*100
pop

 

 

# 병합

#구별 컬럼을 이용해서 2개의 frame을 합치기
df = pd.merge(cctv, pop, on='구별')

 

 

# 불필요한 컬럼 제거

del df['2011년 이전']
del df['2012년']
del df['2013년']
del df['2014년']
del df['2015년']
del df['2016년']
del df['2017년']
del df['기간']

 

 

# 인덱스 재설정

df.set_index('구별', inplace=True)
df

 

 

 

 

 

 

4. 시각화

font_name = font_manager.FontProperties(fname="c:/Windows/Fonts/malgun.ttf").get_name()
rc('font', family=font_name)
df['소계'].plot(kind='barh', grid=True, figsize=(10,10))
plt.show()
df['소계'].sort_values().plot(kind='barh', grid=True, figsize=(5,5))
plt.show()
df['cctv비율'] = df['소계']/df['계'] * 100
df['cctv비율'].sort_values().plot(kind='barh', grid=True, figsize=(5,5))
plt.show()

 

 

# 시각화 - 산포도

plt.figure(figsize=(6,6))
plt.scatter(df['계'], df['소계'], s=50)
plt.xlabel('인구수')
plt.ylabel('CCTV개수')
plt.grid()
plt.show()

 

 

# 시각화 - 기울기와 y절편 구해서 라인 그리기

fp1 = np.polyfit(df['계'], df['소계'], 1)
f1 = np.poly1d(fp1)
fx = np.linspace(100000, 700000, 100)

plt.figure(figsize=(5,5))
plt.scatter(df['계'], df['소계'], s=50)
plt.plot(fx, f1(fx), ls='dashed', lw=3, color='g')
plt.xlabel('인구수')
plt.ylabel('CCTV')
plt.grid()
plt.show()

 

 

#오차 표시

fp1 = np.polyfit(df['계'], df['소계'], 1)
f1 = np.poly1d(fp1)
fx = np.linspace(100000, 700000, 100)
df['오차'] = np.abs(df['소계'] - f1(df['계']))
plt.figure(figsize=(14,10))
plt.scatter(df['계'], df['소계'], c=df['오차'], s=50)
plt.plot(fx, f1(fx), ls='dashed', lw=3, color='g')

for n in range(24):
    plt.text(df['계'][n]*1.02, df['소계'][n]*0.98,
             df.index[n], fontsize=12)

plt.xlabel('인구수')
plt.ylabel('인구당비율')
plt.colorbar()
plt.grid()
plt.show()

[Python] 딥러닝 _ Keras

0ㅑ채
|2024. 3. 20. 12:04

1. Keras

  • 모든 종류의 신경망을 손쉽게 만들고 훈련, 평가, 실행할 수 있는 고수준 딥러닝 API
  • API 문서는 https://keras.io
  • 거의 모든 딥러닝 라이브러리에서 사용 가능

 

 

 

2. Keras 의 Dense

- 완전 연결 층을 만들기 위한 클래스

  • 완전 연결 층 : 이전 층의 모든 연산을 받아들이는 층
import tensorflow as tf
from tensorflow import keras

 

 

2-1) 생성할 때 파라미터

  • unit: 뉴런의 개수
  • activation: 활성화 함수로 기본값은 None 이고 sigmoid, softmax(다중 분류 문제에 사용), tanh(하이퍼볼릭 탄젠트 함수), relu 등을 설정할 수 있다.
  • input_shape 는 입력 층(첫번째 층)의 경우 입력되는 데이터의 크기를 지정해야 하는 매개변수

 

- 샘플 데이터 생성

X = np.arange(1, 6)
y = 3 * X + 2
print(X)
print(y)
[1 2 3 4 5]
[ 5  8 11 14 17]

 

 

 

2-2) Tensorflow의 Keras 모델 생성 방법

- Sequential API

- Functional API

- SubClassing

 

 

2-3) Sequential API 활용

  • 층을 이어 붙이듯 시퀀스에 맞게 일렬로 연결하는 방식
  • 입력 레이어부터 출력 레이어까지 순서를 갖는 형태
  • 입력 레이어가 첫번째 레이어가 되는데 입력 데이터가 이 레이어 투입되고 순서대로 각 층을 하나씩 통과하면서 딥러닝 연산을 수행
  • 이해하기가 가장 쉬운 방법이지만 2개 이상의 다중 입력이나 다중 출력을 갖는 복잡한 구조를 만들 지 못함

 

- 모델 구조

  •  list 이용
    model = tf.keras.Sequential([
                  tf.keras.layers.Dense(10),
                  tf.keras.layers.Dense(5),
                  tf.keras.layers.Dense(1)
    ])
  • add 함수 이용
    model = tf.keras.Sequential()
    model.add(tf.keras.layers.Dense(10))
    model.add(tf.keras.layers.Dense(5))
    model.add(tf.keras.layers.Dense(1))

 

- 입력 데이터 형태 지정

  • 첫번째 층의 input_shape 에 튜플이나 리스트 형태로 설정
    • shape 가 150, 4 인 경우 (150, 4), [150, 4] 도 가능하고 (4, ), [4] 도 가능
    • 첫번째 숫자가 데이터의 개수이고 나머지 부분이 데이터의 shape 가 되므로 데이터의 개수를 생략하더라도 shape 가 설정되면 데이터의 개수는 유추할 수 있기 때문

 

- 단순 선형 회귀 모델 생성

  • Layer가 1개면 가능
  • 입력 데이터는 피처가 1개
  • 출력도 하나의 숫자

    첫번째 층은 입력 구조가 필수
    마지막 층의 뉴런의 개수는 출력하는 데이터의 피처 개수
model = tf.keras.Sequential([
    tf.keras.layers.Dense(1, input_shape=(1,))
])

 

- Sequential 모델은 모델의 구조를 확인하는 것이 가능 - summary()

#모델의 구조 확인
model.summary()
  • 각 층에 대한 정보 리턴

 

- 컴파일

  • 컴파일 과정에서는 Optimizer, Loss Function(손실 함수로 일반적으로 평가 지표를 작성), Metrics(평가 지표를 작성하는데 여기는 list가 가능) 를 지정
  • 작성을 할 때는 긴문자열, 짧은 문자열, 클래스 인스턴스 3가지 방법이 가능한데 문자열로 설정하는 경우는 세부적인 파라미터 조정이 안되고 클래스 인스턴스를 이용하면 세부 파라미터 조정이 가능

# 모델 컴파일

model.compile(optimizer='sgd', loss='mean_squared_error',
             metrics=['mean_squared_error', 'mean_absolute_error']) #긴 문자열 사용

model.compile(optimizer='sgd', loss='mse',
             metrics=['mse', 'mae']) #짧은 문자열 사용

model.compile(optimizer=tf.keras.optimizers.SGD(learning_rate=0.005),
             loss = tf.keras.losses.MeanSquaredError(),
             metrics = [tf.keras.metrics.MeanSquaredError(), 
                        tf.keras.metrics.MeanAbsoluteError])

 

# 훈련

  • 훈련을 하면 가중치를 업데이트
  • 훈련 데이터 세트의 입력(X) 과 정답에 해당하는 출력(y) 그리고 반복 훈련 횟수에 해당하는 epoch 를 설정하는데 기본적으로 훈련을 할 때 마다 손실 과 평가 지표를 출력하는데 verbose=0 을 추가하면 중간 과정이 생략 
  • validation_data 에 검증 데이터를 설정하면 검증 데이터에 대한 손실 과 평가 지표도 같이 반환
  • fit 함수가 리턴하는 객체는 epoch 별 손실 과 평가지표를 dict로 저장하고 있다.
history = model.fit(X, y, epochs=1000, verbose=0)

#훈련 과정에서 발생한 손실 과 평가지표를 시각화
plt.plot(history.history['loss'], label='loss')
plt.legend()
plt.show()

 

- 검증 및 예측

  • 검증은 evaluate 메서드에 훈련 데이터를 입력하면 됩니다.
model.evaluate(X, y)

 

- 예측

  • predict(ndarray 나 tensor로 대입
model.predict(np.array([10]))

 

 

 

2-4) Classification 분류 

  • 데이터가 어느 범주(Category)에 해당하는지 판단하는 문제
  • 회귀가 알고리즘의 퍼포먼스를 확인하기 위해서는 잔차 제곱의 합(SSE) 나 잔차 제곱의 평균(MSE) 등을 사용하고 이 값들은 일반적으로 실수
  • 분류에서는 같은 목적으로 예측이 정답을 얼마나 맞혔는지에 대한 정확도를 측정
    • 정확도는 보통 퍼센트로 나타내고 이 수치는 직관적이기 때문에 머신러닝 알고리즘의 벤치마크 역할을 분류를 가지고 판단
  • ImageNet 이라는 데이터베이스를 이용해서 이미지의 범주를 분류하는 대회가 있는데 2012년 CNN이 등장하면서 2017 년에 거의 100%를 달성
  • 이항 분류
    • 정답의 범주가 두 개인 분류 문제

 

# 레드와 화이트와인 분류: 이항 분류

2024.03.20 - [Python/Python 실전편] - [딥러닝] Keras 이항분류 _ 레드와 화이트와인 분류

 

 

 

# 패션 이미지 분류

2024.03.20 - [Python/Python 실전편] - [딥러닝] Keras _ 패션 이미지 분류

 

[딥러닝] Keras _ 패션 이미지 분류

Keras 의 내장 데이터 세트 - 종류 boston housing: 보스톤 집값으로 회귀에 사용 cifar10: 이미지 분류 데이터로 종류가 10가지 cifar100: 이미지 분류 데이터로 종류가 100가지 mnist: 손 글씨 데이터 fashion mni

yachae4910.tistory.com

 

 

 

2-5) subclassing

  • Sequential API 나 Functional API는 선언적 방식인데 사용할 층 과 연결 방식을 정의한 후 모델에 데이터를 주입해서 훈련이나 추론을 하는 방식
  • 선언적 방식은 장점이 많은데 모델을 저장하거나 복사 또는 공유하기 쉬우며 모델의 구조를 출력하거나 분석하기도 좋고 프레임워크가 크기를 짐작하고 타입을 확인해서 에러를 일직 발견할 수 있고 디버깅하기도 쉬움
    • 정적이라는 단점이 있음
    • 수정하지 못함
  • subclassing 은 기존의 클래스를 상속받아서 수정해서 사용하는 것
  • Model 클래스를 상속받고 __init__ 메서드에서 필요한 층을 만들고 call 메서드 안에 수행하려는 연산을 기술하고 출력 층을 리턴하도록 작성
  • subclassing 을 하고자 하면 원 클래스에 메서드들의 기능을 확인할 필요가 있습니다.
    • 필요한 메서드를 오버라이딩 해서 사용

 

- 샘플

class WideAndDeepModel(keras.models.Model):
	def __init__(self, 필요한 매개변수, **kwargs):
		super().__init__(**kwargs)
 		self.hidden1 = keras.layers.Dense(units, activation)
		self.hidden2 = keras.layers.Dense(units, activation)
		self.hidden3 = keras.layers.Dense(units, activation)
		self.output = keras.layers.Dense(units)
		
	def call(self, inputs):
		hidden1 = self.hidden1(input_B)
		..
		return self.output


model = WideAndDeepModel(매개변수)

 

 

 

 

2-6) 모델 저장과 복원

  • 모델을 만들고 훈련 한 후 모델.save 함수를 호출하면 모델을 훈련한 상태로 저장할 수 있다.
  • load_model 함수를 이용해서 복원이 가능
  • 복원한 모델을 이용해서 예측을 할 수 도 있고 훈련을 추가로 할 수 있습니다.
    • 복원한 모델을 가지고 추가 훈련을 해서 사용하는 것을 사전 훈련된 모델을 이용한다고 함

 

 

 

 

 

 

 

3. Keras NN

3-1) Sequential API 이용 회귀

# 데이터 가져오기

  • 외부에서 데이터를 가져오면 자료형을 확인
  • 디셔너리라면 모든 key를 확인 - keys()
  • 클래스라면 모든 속성을 확인 - dir
from sklearn.datasets import fetch_california_housing
housing = fetch_california_housing()

print(type(housing))
print(dir(housing))
<class 'sklearn.utils._bunch.Bunch'>
['DESCR', 'data', 'feature_names', 'frame', 'target', 'target_names']

 

 

# 데이터 분할 - 훈련 / 훈련 시 검증 / 테스트

from sklearn.model_selection import train_test_split

X_train_full, X_test, y_train_full, y_test = train_test_split(housing.data, housing.target, random_state=42)

X_train, X_valid, y_train, y_valid = train_test_split(X_train_full, y_train_full, random_state=42)
 

 

 

# 정규화

from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()

X_train = scaler.fit_transform(X_train)
X_valid = scaler.fit_transform(X_valid)
X_test = scaler.fit_transform(X_test)

print(X_train.shape)
(11610, 8)

 

 

# 회귀 모델 만들기

  • Sequential API를 이용해서 회귀용 MLP을 구축, 훈련, 평가, 예측하는 방법은 분류에서 했던 것 과 매우 비슷하지만
  • 차이점은 출력 층이 활성화 함수가 없는 하나의 뉴런을 가져야 한다는 것, 손실 함수가 평균 제곱 오차나 평균 절대값 오차로 변경을 해야 함
import tensorflow as tf
from tensorflow import keras

#회귀 모델 만들기
#input_shape 설정할 때 데이터의 개수는 생략
model = keras.models.Sequential([
    keras.layers.Dense(30, activation="relu", input_shape=X_train.shape[1:]),
    keras.layers.Dense(15, activation="relu"),
    keras.layers.Dense(units=1)
])

model.summary()
Model: "sequential"

 

 

# 모델 컴파일 및 훈련

model.compile(loss="mean_squared_error", 
              optimizer=keras.optimizers.SGD(learning_rate=0.001))
history = model.fit(X_train, y_train, epochs=20,
                   validation_data = (X_valid, y_valid))


Epoch 20/20
363/363 ━━━━━━━━━━━━━━━━━━━━ 1s 1ms/step - loss: 0.3844 - val_loss: 0.4524

 

 

# 모델 평가

print(model.evaluate(X_test, y_test))

 

 

# 예측

X_new = X_test[:3]
y_pred = model.predict(X_new)
print(y_pred)

 

 

- Sequential API

  • 층을 연결하고 모든 데이터가 순차적으로 층을 통과하면서 출력을 만드는 방식

 

 

 

3-2) Functional API를 활용한 회귀

- Functional API

  • 일반적 MLP(Sequential API)는 네트워크에 있는 층 전체에 모든 데이터를 통과시키는데 이렇게 하면 간단한 패턴이 연속적인 변환으로 인해서 왜곡 될 수 있음
  • 입력의 일부 또는 전체를 출력 층에 바로 연결하는 방식
    • MLP가 전체를 통과하기도 하고 일부분만 통과하기도 하기 때문에 복잡한 패턴 과 단순한 패턴 모두를 학습해서 더 좋은 성과를 내기도 합니다.
  • 함수형 API를 사용하게 되면 입력 데이터를 설정
    • 층 간의 결합도 직접 설정

 

- 함수형 API를 이용해서 모델 만들기

#모델 만들기
input_ = keras.layers.Input(shape=X_train.shape[1:])

#input_ 층의 출력을 받아서 수행하는 층
hidden1 = keras.layers.Dense(30, activation="relu")(input_)
hidden2 = keras.layers.Dense(15, activation="relu")(hidden1)

#2개의 층 합치기
concat = keras.layers.concatenate([input_, hidden2])

#출력 층 생성
output = keras.layers.Dense(1)(concat)

model = keras.models.Model(inputs=[input_], outputs=[output])
  • 함수형 API의 모델을 만들 때는 input 과 output에 list를 대입한다.
  • 여러 개의 입력 과 여러 개의 출력 사용이 가능하다.
  • 객체 탐지 와 같은 문제를 해결할 때는 출력이 1개가 아니라 여러 개인 경우가 많다.
  • 이런 문제는 Sequential API로는 해결할 수 없다.

 

# 컴파일과 훈련

model.compile(loss="mean_squared_error", 
              optimizer=keras.optimizers.SGD(learning_rate=0.001))
history = model.fit(X_train, y_train, epochs=20,
                   validation_data = (X_valid, y_valid))


Epoch 20/20
363/363 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.4188 - val_loss: 0.4654

 

 

- 입력 경로 여러개 생성

#여러 경로의 input 사용
input_A = keras.layers.Input(shape=[5])
input_B = keras.layers.Input(shape=[6])

hidden1 = keras.layers.Dense(30, activation="relu")(input_B)
hidden2 = keras.layers.Dense(15, activation="relu")(hidden1)

#input_A는 하나의 hidden 층도 통과하지 않은 데이터
# hidden2는 2개의 hidden 층을

#통과한 데이터
concat = keras.layers.concatenate([input_A, hidden2])
output = keras.layers.Dense(1)(concat)

#입력을 2가지를 사용 - 데이터의 다양성을 추가해서 학습
#모든 입력이 hidden layer를 통과하게 되면 깊이가 깊어질 때 데이터의 왜곡 발생 가능
model = keras.models.Model(inputs=[input_A, input_B], outputs=[output])

 

# 모델의 input 데이터 수정해서 입력 데이터 수정

  • 첫번째 입력은 앞의 5개를 사용하고 두번째 입력은 뒤의 6개를 이용 - 전체 데이터 사용
X_train_A, X_train_B = X_train[:, :5], X_train[:, 2:]
X_valid_A, X_valid_B = X_valid[:, :5], X_valid[:, 2:]
X_test_A, X_test_B = X_test[:, :5], X_test[:, 2:]
model.compile(loss="mse", optimizer=keras.optimizers.SGD(learning_rate=0.001))
history = model.fit((X_train_A, X_train_B), y_train,
                   epochs=20, validation_data=((X_valid_A, X_valid_B), y_valid))
mse_test = model.evaluate((X_test_A, X_test_B), y_test)


363/363 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.4468 - val_loss: 0.4863
162/162 ━━━━━━━━━━━━━━━━━━━━ 0s 944us/step - loss: 0.4481

 

 

- 출력을 여러개 생성

필요한 이유: 

  • 그림에 있는 주요 물체를 분류하고 위치를 알아야 하는 경우가 있는데 이 경우는 분류 와 회귀를 동시에 수행해야 하는 경우
  • 다중 분류 작업을 하고자 하는 경우
  • 보조 출력을 사용하고자 할 때
    • 서로 다른 입력을 받아서 딥러닝을 수행한 후 일정 비율을 적용해서 반영

다른 입력을 받아서 출력을 만든 후 0.9:0.1 비율로 반영해서 출력 만들기

  • 손실 함수를 수행할 때 각 출력의 비중을 다르게 반영하도록 하기
  • 여러 경로의 input 사용
input_A = keras.layers.Input(shape=[5])
input_B = keras.layers.Input(shape=[6])

hidden1 = keras.layers.Dense(30, activation="relu")(input_B)
hidden2 = keras.layers.Dense(15, activation="relu")(hidden1)

concat = keras.layers.concatenate([input_A, hidden2])

output = keras.layers.Dense(1)(concat)
aux_output = keras.layers.Dense(1)(hidden2)

model = keras.models.Model(inputs=[input_A, input_B],
                          outputs=[output, aux_output])
#출력이 2개가 된 경우 손실을 적용할 때 비율을 설정하는 것이 가능
#이 경우는 첫번째 출력의 손실을 90% 반영하고 두번째 출력의 손실을 10% 반영
model.compile(loss="mse", optimizer=keras.optimizers.SGD(learning_rate=0.001),
             loss_weights=[0.9, 0.1])

history = model.fit((X_train_A, X_train_B), y_train,
                   epochs=20, validation_data=((X_valid_A, X_valid_B), y_valid))


Epoch 20/20
363/363 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.3993 - val_loss: 0.4345

 

X_new_A, X_new_B = X_test_A[:3], X_test_B[:3]
#출력이 2개 이므로 각 데이터마다 2개의 값을 리턴
y_pred_main, y_pred_aux = model.predict([X_new_A, X_new_B])
print(y_pred_main)
print(y_pred_aux)
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 76ms/step
[[0.4174796]
 [1.8939716]
 [3.5359378]]
[[-0.1951855 ]
 [-0.06544766]
 [-0.6592884 ]]

 

 

 

3-3) SubClassing을 이용하는 방법

  • Model 클래스를 상속받아서 사용하는 방법
  • __init__ 메서드를 재정의해서 필요한 층을 생성
  • call 메서드를 재정의해서 출력층을 리턴

- 장점

  • 모델을 생성할 때 필요한 매개변수를 직접 설정할 수 있기 때문에 동적인 모델을 만들 수 있다.

- 이전과 동일한 모델을 subclassing을 이용해서 구현

  • 상위 클래스를 직접 만들지 않은 경우는 __init__ 메서드에서
  • 상위 클래스의 __init__을 호출해야 합니다.
class WideAndDeepModel(keras.models.Model):
    def __init__(self, units=30, activation="relu", **kwargs):
        #상위 클래스의 초기화 메서드 호출
        super().__init__(**kwargs)
        
        #층을 생성
        self.hidden1 = keras.layers.Dense(units, activation=activation)
        self.hidden2 = keras.layers.Dense(units, activation=activation)
        
        self.main_output = keras.layers.Dense(1)
        self.aux_output = keras.layers.Dense(1)
        
    #두번째 매개변수가 input     
    def call(self, inputs):
        input_A, input_B = inputs
        hidden1 = self.hidden1(input_B)
        hidden2 = self.hidden2(hidden1)
        
        concat = keras.layers.concatenate([input_A, hidden2])
        
        main_output = self.main_output(concat)
        aux_output = self.aux_output(hidden2)
        return main_output, aux_output

 

model = WideAndDeepModel(30, activation="relu")
model.compile(loss="mse", optimizer=keras.optimizers.SGD(learning_rate=0.001),
             loss_weights=[0.9, 0.1])

history = model.fit((X_train_A, X_train_B), y_train,
                   epochs=20, validation_data=((X_valid_A, X_valid_B), y_valid))


Epoch 20/20
363/363 ━━━━━━━━━━━━━━━━━━━━ 1s 1ms/step - loss: 0.3751 - val_loss: 0.4181

 

 

 

 

3-4) CallBack

- 어떤 사건이 발생했을 때 특정 작업을 수행하도록 하는 것

 

- 조기종료

  • epochs 을 크게 지정하면 훈련을 많이 수행하기 때문에 성능이 좋아질 가능성이 높은데 늘리게 되면 훈련 시간이 오래 걸린다.
  • 일정 에포크 동안 검증 세트에 대한 점수가 향상되지 않으면 훈련을 멈추도록 할 수 있다.
  • keras.callbacks.EarlyStopping 클래스의 인스턴스를 만들 때 patience 매개변수에 원하는 에포크 지정해서 만들고 모델이 fit 메서드를 호출할 때 callbacks 파라미터에 list 형태로 대입하면 됨
#5번의 epoch 동안 점수가 좋아지지 않으면 조기 종료
early_stopping_cb = keras.callbacks.EarlyStopping(patience=5)

history = model.fit((X_train_A, X_train_B), y_train,
                   epochs=100, validation_data=((X_valid_A, X_valid_B), y_valid),
                   callbacks=[early_stopping_cb])



Epoch 45/100
363/363 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.3407 - val_loss: 0.3985

 

 

- 학습률 스케줄러

  • 학습률을 동적으로 변화시켜서 수행하는 것이 가능
  • LearningRateScheduler 라는 클래스를 이용하는데 이 때는 에포크 와 학습율을 매개변수로 갖는 함수를 만들어서 인스턴스를 생성할 때 대입
#5번의 epoch 동안 점수가 좋아지지 않으면 조기 종료
early_stopping_cb = keras.callbacks.EarlyStopping(patience=5)

#5번의 epoch 동안은 기존 학습률을 유지하고 그 이후에는 학습률을 감소시키는 함수
def scheduler(epoch, lr):
    if epoch < 5:
        return lr
    else:
        lr = lr - 0.0001
        return lr
#학습률을 동적으로 변화시키는 체크포인트    
lr_scheduler = tf.keras.callbacks.LearningRateScheduler(scheduler)

history = model.fit((X_train_A, X_train_B), y_train,
                   epochs=100, validation_data=((X_valid_A, X_valid_B), y_valid),
                   callbacks=[early_stopping_cb, lr_scheduler])

 

 

- 모델 저장과 복원

  • 딥러닝은 fit 함수를 호출한 후 다음에 다시 fit을 호출하면 이전에 훈련한 이후 부터 다시 훈련하는 것이 가능
  • 기존 모델을 저장하고 이를 읽어들여서 재훈련이 가능

 

# 모델 저장

  • save 함수를 이용해서 저장
  • 모델을 저장할 때 loss 는 반드시 full name으로 작성
  • 버전에 따라서는 확장자를 제한하는 경우도 있다.
    • 최신 버전에서 이 부분이 적용
model = keras.models.Sequential([
    keras.layers.Dense(30, activation="relu", input_shape=[8]),
    keras.layers.Dense(15, activation="relu"),
    keras.layers.Dense(1)
])

model.compile(loss="mse", optimizer=keras.optimizers.SGD(learning_rate=0.001))
history=model.fit(X_train, y_train, epochs=10, validation_data=(X_valid, y_valid))
mse_test = model.evaluate(X_test, y_test)
#모델 저장
model.save("my_model.keras")

 

 

- 모델 가져오기

  • load 함수 이용

# 모델 복원

m = keras.models.load_model("my_model.keras")

#복원 된 모델과 기존의 모델이 같은 결과를 만들어 냅니다.
print(m.predict(X_new))
print(model.predict(X_new))
  •  복원된 모델과 기존의 모델이 같은 결과를 만들어냄

 

 

- 모델의 가중치를 저장하고 복원

  • save_weights 와 load_weights를 이용
model.save_weights("my_model.weights.h5")
model.load_weights("my_model.weights.h5")

 

 

- 모델을 저장하는 체크 포인트 사용

  • ModelCheckPoint 라는 클래스를 이용해서 모델의 이름을 설정
  • 체크 포인트를 만들 때 이름만 설정하면 마지막 모델이 저장
    • 마지막 모델은 최상의 모델이 아닐 수 있다.
    • save_best_only=True를 설정하면 최상의 모델을 저장한다.
checkpoint_cb = keras.callbacks.ModelCheckpoint('my_model.keras',
                                               save_best_only=True)
history = model.fit(X_train, y_train, epochs=10,
                   validation_data=(X_valid, y_valid),
                   callbacks=[checkpoint_cb])

 

 

 

3-5) 사용자 정의 콜백

- Callback 클래스를 상속받아서 원하는 콜백을 생성할 수 있는 기능

- on_train_begin, on_train_end, on_epoch_begin, on_epoch_end, on_batch_bigin, on_batch_end 이 메서드들은 훈련전후 그리고 한 번의 에포크 전후 그리고 배치 전후에 작업을 수행시키고자 하는 경우에 사용

  • on_test 로 시작하는 메서드를 오버라이딩 하면 검증 단계에서 작업을 수행

 

 

 

3-6) 신경망의 하이퍼 파라미터 튜닝

- 신경망의 유연성은 단점이 되기도 하는데 조정할 하이퍼 파라미터가 많음

- 복잡한 네트워크 구조에서 뿐 만 아니라 간단한 다층 퍼셉트론에서도 층의 개수, 층마다 존재하는 뉴런의 개수, 각 층에서 사용하는 활성화 함수, 가중치 초기화 전략 등 많은 것을 바꿀 수 있는데 어떤 하이퍼 파라미터 조합이 주어진 문제에 대해서 최적인지 확인

- 이전 머신러닝 모델들은 GridSearchCV 나 RandomizedSearchCV를 이용해서 하이퍼 파라미터 공간을 탐색할 수 있었는데 딥러닝에서는 이 작업을 할려면 Keras 모델을 sklearn 추정기 처럼 보이도록 바꾸기

 

 

- 신경망의 하이퍼 파라미터 튜닝

keras.backend.clear_session()
np.random.seed(42)
tf.random.set_seed(42)

 

 # 딥러닝 모델을 만드는 함수

def build_model(n_hidden=1, n_neurons=30, learning_rate=0.003, input_shape=[8]):
    model = keras.models.Sequential()
    #입력 레이어 추가
    model.add(keras.layers.InputLayer(input_shape=input_shape))
    #n_hidden 만큼 히든 층 추가
    for layer in range(n_hidden):
        model.add(keras.layers.Dense(n_neurons, activation="relu"))
    #출력 층 추가
    model.add(keras.layers.Dense(1))
    #최적화 함수 생성
    optimizer = keras.optimizers.SGD(learning_rate = learning_rate)
    model.compile(loss="mean_squared_error", optimizer=optimizer, metrics=['mse'])
    return model

 

# 하이퍼 파라미터 튜닝을 위해 scikit-learn 추정기로 딥러닝 모델 생성

!pip install scikeras
from scikeras.wrappers import KerasRegressor

keras_reg = KerasRegressor(build_model())


from sklearn.model_selection import RandomizedSearchCV

param_distribs = {
    "epochs":[100, 200, 300, 400, 500, 600]
}

#랜더마이즈드 cv를 생성
rnd_search_cv = RandomizedSearchCV(keras_reg, param_distribs, n_iter=10, cv=3,
                                  verbose=2)
#하이퍼파라미터 튜닝
rnd_search_cv.fit(X_train, y_train, epochs=100,
                 validation_data=(X_valid, y_valid),
                 callbacks=[keras.callbacks.EarlyStopping(patience=10)])

 

 

 

- 은닉 층 개수

  • 이론적으로는 은닉 층이 하나인 다층 퍼셉트론이라도 뉴런 개수가 충분하다면 아주 복잡한 함수도 모델링할 수 있지만 심층 신경망이 얕은 신경망보다 파라미터 효율성이 좋다. 
  • 심층 신경망은 복잡한 함수를 모델링하는데 얕은 신경망보다 적은 수의 뉴런을 사용하기 때문에 동일한 양의 훈련 데이터에서 더 높은 성능을 낼 수 있다.
  • 심층 신경망을 만들면 아래쪽 은닉 층은 저수준의 구조를 모델링하고 
    중간 은닉 층은 저수준의 구조를 연결해서 중간 수준의 구조를 모델링하고 
    가장 위쪽 은닉 층 과 출력 층은 중간 수준의 구조를 연결해서 고수준의 구조를 모델링 
  •  MNIST 데이터 세트에서 하나의 은닉층에 수백개의 뉴런을 사용한 경우 정확도가 97% 정도 나왔는데 동일한 수의 뉴런을 사용하고 은닉층을 2개로 늘렸을 때 98% 정도의 정확도
  • 복잡한 문제들은 훈련 세트에 과대 적합이 될 때 까지 점진적으로 은닉 층의 개수를 늘릴 수 있는데 대규모 이미지 분류나 음성 인식 분야에서는 수십개에서 수백개의 은닉층을 가지게 되는데 이런 경우는 훈련 데이터가 아주 많이 필요함
    • 일반적으로 이런 네트워크를 처음부터 훈련하는 경우는 거의 없음
    • 비슷한 작업에서 가장 뛰어난 성능을 낸 미리 훈련된 네트워크 일부를 재사용

 

- 은닉 층의 뉴런 개수

  • 입력 층 과 출력 층의 뉴런의 개수는 수정할 수 없음
  • 은닉 층의 뉴런의 개수는 일반적으로 각 층의 뉴런의 개수를 줄여가면서 깔대기처럼 구성하는데 저수준의 많은 특성이 고수준의 적은 특성으로 합쳐질 수 있기 때문
  • 뉴런의 개수는 일단 많이 가지고 시작을 하다가 조기 종료 나 규제를 이용해서 조정

 

- 학습률

  • 좋은 학습률을 찾는 방법은 아주 작은 학습률(0.00001) 에서 시작해서 점진적으로 큰 학습률(10) 까지 수백 번 반복하여 모델을 훈련
    • 보통의 경우는 0.00001 ~ 10 까지 exp(log (10의 6승 / 500)) 을 곱해가면서 조정

 

- Optimiazer

  • SGD 보다는 최근에 등장한 Optimizer 사용을 권장

 

- Batch_Size

  • 32 이하의 크기를 사용하는 권장하지만 최근에는 학습률을 작은 값에서 큰 값으로 변경해가면서 수행하는 경우는 192 정도의 크기도 괜찮다.
  • 배치 크기가 커지면 훈련 시간을 단축할 수 있음
  • 학습률을 변경하면서 수행할 때 큰 배치 크기를 사용하고 훈련이 불안정해지면 작은 배치 크기를 선택

 

- 활성화 함수

  • 일반적으로 ReLU 가 좋은 값

 

- 반복 횟수

  • 대부분 크게 설정하고 조기 종료를 설정

 

 

3-7) 딥러닝에서의 문제점과 해결책

- 그라디언트 소실 과 폭주 문제

  • 역전파 알고리즘: 
    출력 층에서 입력 층으로 오차 그라디언트를 전파하면서 진행되는데 알고리즘이 신경망의 모든 파라미터에 대한 오차 함수의 그라디언트를 계산하면 경사 하강법 단계에서 이 그라디언트를 사용해서 파라미터를 수정
  • vanishing gradient(그라디언트 소실) :
    알고리즘이 하위 층으로 진행될수록 그라디언트는 점점 작아지는 경우가 많은데 경사 하강법이 하위 층의 연결 가중치를 변경하지 않은채로 둔다면 훈련이 좋은 솔루션을 찾지 못하게 되는 현상.
  • exploding gradient(폭주):
    그라디언트가 점점 더 커져서 여러 층이 비정상적으로 큰 가중치를 가지되면 알고리즘은 발산한다고 하는데  RNN에서 주로 나타남
  • 원인
    • 활성화 함수로 로지스틱 시그모이드 함수를 사용하고 가중치 초기화를 할 때 평균이 0이고 표준 편차가 1인 정규 분포에서 추출한 값으로 한 것
    • 로지스틱 시그모이드 함수는 평균이 0이 아니고 0.5 이기 때문에 이 문제가 발생한다고 보고 하이퍼볼릭 탄젠트 함수(평균이 0)를 사용

 

- 글로럿과 He 초기화

  • 딥러닝 초창기에는 가중치 초기화를 할 때 평균이 0이고 표준 편차가 1인 정규 분포에서 추출한 값을 가지고 사용
  • 글러럿 초기화 또는 세이비어 초기화
    • 평균이 0이고 분산이 1/fan(avg) 인 정규 분포 나 제곱근(3/fan(avg)) 한 값에 -1 과 1을 곱한 값의 균등 분포에서 초기화
      fan-in: 입력의 개수
      fan-out: 출력의 개수
      fan(avg) = (fan-in + fan-out)/2
    • 훈련 속도도 향상
    • fan(avg) 대신에 fan-in을 사용하면 르쿤 초기화라고 함
  • He 초기화
    • ReLU와 그 변종들
    • 2/fan-in 인 정규분포를 가지고 초기화
  • keras 에서 지원하는 목록: https://www.tensorflow.org/api_docs/python/tf/keras/initializers
  • 기본값은 Glorot 초기화 인데 다른 방법을 사용하고자 하면 kernel_initializer="초기화 방법" 을 지정하거나 클래스의 인스턴스로 지정
  • 지원 가능한 목록
    • print([name for name in dir(keras.initializers) if not name.startswith("_")])
  • 사용
    • keras.layers.Dense(10, activation="relu", kernel_initializer = "he_normal")

 

- 수렴하지 않는 활성화 함수

  • 딥러닝 모델을 만들 때 잘못된 활성화 함수를 선택하면 그라디언트 소실이나 폭주 문제가 발생할 수 있음
  • 초창기에는 sigmoid 함수가 최선이라고 생각을 했지만 이후에 다양한 활성화 함수가 등장
  • 많이 사용되는 것은 relu, softmax, tanh 등ㅍ
  • ReLU
    • 특정 양수 값에 수렴하지 않는다는 장점이 있음
    • 계산이 빠름
    • 학습률을 너무 높게 설정하면 가중치의 합이 음수가 되서 뉴런이 죽어 버림
    • 뉴런이 죽는 문제를 해결하기 위해서 max(알파*x, x)를 적용하는 방식이 있는데 이것이 LeakyReLU
    • 활성화 함수로 적용해도 되지만 층을 만들어서 추가해도 됨
    • 최근의 Tensorflow에서 제공하는 Keras API는 문자열로 설정하는 부분을 인스턴스를 생성해서 적용하는 부분으로 변경되고 있다.
  • LeakyReLU를 적용한 패션 MNIST 분류

# 활성화 함수의 초기화 변경

(X_train_full, y_train_full), (X_test, y_test)  = keras.datasets.fashion_mnist.load_data()
X_train_full = X_train_full /  255.0
X_test = X_test / 255.0

X_valid, X_train = X_train_full[:5000], X_train_full[5000:]
y_valid, y_train = y_train_full[:5000], y_train_full[5000:]
tf.random.set_seed(42)
np.random.seed(42)

model = keras.models.Sequential([
    keras.layers.Flatten(input_shape=[28, 28]),
    keras.layers.Dense(250, kernel_initializer="he_normal"),
    keras.layers.LeakyReLU(),
    keras.layers.Dense(100, kernel_initializer="he_normal"),
    keras.layers.LeakyReLU(),
    keras.layers.Dense(50,kernel_initializer="he_normal"),
    keras.layers.LeakyReLU(),
    keras.layers.Dense(10, activation="softmax"),
])

model.compile(loss="sparse_categorical_crossentropy",
             optimizer=keras.optimizers.SGD(learning_rate=0.001),
             metrics=['accuracy'])
history = model.fit(X_train, y_train, epochs=10,
                   validation_data=(X_valid, y_valid))
print(model.evaluate(X_test, y_test))
313/313 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - accuracy: 0.8340 - loss: 0.4829
[0.4901731014251709, 0.8281000256538391]

 

 

- 알파를 무작위로 선택하고 테스트 할 때는 평균을 사용하는 방식인 RReLU 방식도 제안

- 알파 값을 훈련하는 동안 역전파에 의해서 변경하는 PReLU 도 등장

  • 대규모 이미지 세트에서는 매우 잘 동작을 하는데 소규모 데이터에서는 과대 적합이 발생하는 경우가 있음

- PReLU를 적용해서 비교

#PReLU 사용
tf.random.set_seed(42)
np.random.seed(42)

model = keras.models.Sequential([
    keras.layers.Flatten(input_shape=[28, 28]),
    keras.layers.Dense(250, kernel_initializer="he_normal"),
    keras.layers.PReLU(),
    keras.layers.Dense(100, kernel_initializer="he_normal"),
    keras.layers.PReLU(),
    keras.layers.Dense(50,kernel_initializer="he_normal"),
    keras.layers.PReLU(),
    keras.layers.Dense(10, activation="softmax"),
])

model.compile(loss="sparse_categorical_crossentropy",
             optimizer=keras.optimizers.SGD(learning_rate=0.001),
             metrics=['accuracy'])
history = model.fit(X_train, y_train, epochs=10,
                   validation_data=(X_valid, y_valid))
print(model.evaluate(X_test, y_test))

 

 

- ELU 함수

  • ReLU 와 유사한데 0보다 작을 때 알파(exp(데이터) - 1) 로 계산
  • Dense 층에 activation 매개변수에 "elu"로 바로 적용 
  • 훈련 속도는 좋은데 검증 속도가 느림

 

#PReLU 사용

tf.random.set_seed(42)
np.random.seed(42)

model = keras.models.Sequential([
    keras.layers.Flatten(input_shape=[28, 28]),
    keras.layers.Dense(250, kernel_initializer="he_normal", activation="relu"),
    keras.layers.Dense(100, kernel_initializer="he_normal", activation="relu"),
    keras.layers.Dense(50,kernel_initializer="he_normal", activation="relu"),
    keras.layers.Dense(10, activation="softmax"),
])

model.compile(loss="sparse_categorical_crossentropy",
             optimizer=keras.optimizers.SGD(learning_rate=0.001),
             metrics=['accuracy'])
history = model.fit(X_train, y_train, epochs=10,
                   validation_data=(X_valid, y_valid))
print(model.evaluate(X_test, y_test))

 

 

- SELU

  • Scaled ELU
  • 출력을 평균이 0이고 표준 편차가 1이 되도록 만들어주는 활성화 함수
  • 다른 활성화 함수보다 뛰어난 성능을 발휘
  • 제약조건
    • 입력 특성이 반드시 표준화 되어 이어야 함(평균이 0이고 표준편차가 1)
    • 가중치 초기화는 르쿤 초기화가 되어 있어야 함
    • Sequential API로 생성된 모델이어야 함
  • 단점
    • 훈련 시간이 오래 걸림
  • activation 매개변수에 selu를 설정

- 컴퓨팅 자원이 허락한다면 여러가지 초기화 방법을 적용해서 훈련을 하면 의미있는 결과를 얻어 낼 수 있다.

 

 

- 배치 정규화

  • 활성화 함수를 변경하고 초기화 기법을 변경을 해서 그라디언트 소실 문제 나 폭주 문제를 어느 정도 해결할 수 있는데 훈련을 많이 하게 되면 완전히 해결했다고 보기는 어려움
  • 활성화 함수에 대입하기 전 이나 후에 데이터를 원점에 맞추고 정규화 한 다음 2개의 새로운 파라미터를 추가해서 값의 스케일을 조정하고 이동시킴
  • 새로 만들어진 2개의 파라미터는 하나는 스케일 조정에 사용하고 하나는 원점 이동에 사용
  • 배치 정규화 층을 신경망의 첫번째 층으로 사용하면 훈련 세트를 표준화 할 필요가 없음
  • 이 역할을 BatchNoramlization 이라는 층을 이용해서 수행
model = keras.models.Sequential([
    keras.layers.Flatten(input_shape=[28, 28]),
    
    keras.layers.BatchNormalization(),
    keras.layers.Dense(250, activation="relu"),
    
    keras.layers.BatchNormalization(),
    keras.layers.Dense(100, activation="relu"),
    
    keras.layers.BatchNormalization(),
    keras.layers.Dense(50, activation="relu"),
    
    keras.layers.BatchNormalization(),
    keras.layers.Dense(10, activation="softmax"),
])

model.compile(loss="sparse_categorical_crossentropy",
             optimizer=keras.optimizers.SGD(learning_rate=0.001),
             metrics=['accuracy'])
history = model.fit(X_train, y_train, epochs=10,
                   validation_data=(X_valid, y_valid))
print(model.evaluate(X_test, y_test))
  • 배치 정규화를 이용하면 일부 뉴런이 죽는 현상을 거의 해소하기 때문에 성능이 좋아질 가능성이 높다.

 

 

- 그라디언트 클리핑

  • 역전파 될 때 일정 임계값을 넘어서는 못하게 그라디언트를 잘라 내는 것
  • RNN 에서는 배치 정규화를 사용하기가 어려움
    • 이 경우에는 Optimizer를 만들 때 clipvalue 나 clipnorm 매개변수를 이용해서 Gradient 값을 제한하는 방법으로 유사한 효과를 나타낸다.
    • 배치 정규화가 -1 ~ 1 사이의 값으로 정규화하므로 임계값은 1.0을 사용한다.
    • 그라디언트의 값이 -1 보다 작거나 1보다 크면 -1 이나 1로 수정해서 기울기가 소실되는 것을 방지

 

 

- 사전 훈련된 층 재사용

  • 전이학습 (Transfer Learning)  : 큰 규모의 NN을 처음부터 훈련시키는 것은 많은 자원을 소모하게 되는데 이런 경우 해결하려는 것과 비슷한 유형의 문제를 처리한 신경망이 있는지 확인해보고 그 신경망의 하위층을 재사용하는 것이 효율적
  • 전이 학습을 이용하게 되면 훈련 속도를 개선할 수 있고 훈련 데이터의 양도 줄어들게 된다.
  • 동일한 이미지 분류 모델이라면 하위 층에서 유사하게 점이나 선 들의 모형을 추출하는 작업을 할 가능성이 높으므로 하위 층을 공유해도 유사한 성능을 발휘하게 된다. 
  • 출력 층은 하고자 하는 마지막 작업이 서로 다를 가능성이 높고(ImageNet 의 이미지를 분류하는 모델은 20,000 개의 카테고리를 가지고 있고 우리는 개와 고양이를 분류하는 이진 분류의 경우) 상위 층은 거의 출력 과 유사한 형태가 만들어 진 것이므로 역시 재사용할 가능성은 낮다.
  • 샌달 과 셔츠를 제외한 모든 이미지를 가지고 분류 모델을 만들고 이를 이용해서 샌달과 셔츠 이미지 중 200개 만 가진 작은 훈련 세트를 훈련해서 정확도를 확인

 

#데이터 셋 분할

def split_dataset(X, y):
    y_5_or_6 = (y == 5) | (y == 6)
    #5 나 6이 아닌 데이터
    y_A = y[~y_5_or_6]
    y_A[y_A > 6] -= 2 #6보다 큰 레이블은 2를 빼서 연속된 레이블로 만들기
    
    y_B = (y[y_5_or_6] == 6).astype(np.float32)
    return ((X[~y_5_or_6], y_A), (X[y_5_or_6], y_B))
(X_train_A, y_train_A), (X_train_B, y_train_B) = split_dataset(X_train, y_train)
(X_valid_A, y_valid_A), (X_valid_B, y_valid_B) = split_dataset(X_valid, y_valid)
(X_test_A, y_test_A), (X_test_B, y_test_B) = split_dataset(X_test, y_test)

X_train_B = X_train_B[:200]
y_train_B = y_train_B[:200]
X_train_A.shape #훈련 데이터 전체
(43986, 28, 28)
X_train_B.shape #훈련 데이터 중 200개
(200, 28, 28)
y_train_A[:50] #8가지 모양
array([4, 0, 5, 7, 7, 7, 4, 4, 3, 4, 0, 1, 6, 3, 4, 3, 2, 6, 5, 3, 4, 5,
       1, 3, 4, 2, 0, 6, 7, 1, 3, 7, 0, 3, 7, 4, 2, 7, 0, 6, 3, 3, 2, 2,
       0, 6, 3, 1, 7, 1], dtype=uint8)
y_train_B[:50] #2가지 모양
array([1., 1., 0., 0., 0., 0., 1., 1., 1., 0., 0., 1., 1., 0., 0., 0., 0.,
       0., 0., 1., 1., 0., 0., 1., 1., 0., 1., 1., 1., 1., 1., 0., 1., 0.,
       1., 1., 1., 0., 1., 0., 0., 0., 1., 1., 1., 0., 1., 0., 0., 0.],
      dtype=float32)

 

- 많은 양의 데이터로 모델 훈련

  • 이 작업은 실제로는 우리는 검색하고 모델을 load 하면 됨
tf.random.set_seed(42)
np.random.seed(42)

#모델 생성
model_A = keras.models.Sequential()

#입력 층
model_A.add(keras.layers.Flatten(input_shape=[28, 28]))

#히든 층
for n_hidden in (300, 100, 50, 50, 50):
    model_A.add(keras.layers.Dense(n_hidden, activation="selu"))

#출력 층
model_A.add(keras.layers.Dense(8, activation="softmax"))


model_A.compile(loss = "sparse_categorical_crossentropy",
               optimizer=keras.optimizers.SGD(learning_rate=0.001),
               metrics=['accuracy'])

history = model_A.fit(X_train_A, y_train_A, epochs=20,
                     validation_data=(X_valid_A, y_valid_A))

 

# 모델 저장

model_A.save("my_model_A.keras")
#실제 해결을 하고자 하는 모델
model_B = keras.models.Sequential()
#입력 층
model_B.add(keras.layers.Flatten(input_shape=[28, 28]))
#히든 층
for n_hidden in (300, 100, 50, 50, 50):
    model_B.add(keras.layers.Dense(n_hidden, activation="selu"))
#출력 층: 2개를 분류하는 것은 2가지 방법이 있음
#1일 확률을 구하는 것 과 0 과 1일 확률을 구하는 것
model_B.add(keras.layers.Dense(1, activation="sigmoid"))
model_B.compile(loss = "binary_crossentropy",
               optimizer=keras.optimizers.SGD(learning_rate=0.001),
               metrics=['accuracy'])
history = model_B.fit(X_train_B, y_train_B, epochs=20,
                     validation_data=(X_valid_B, y_valid_B))

 

- 기존 모델인 model_A를 이용해서 해결

#기존 모델 가져오기
model_A = keras.models.load_model("my_model_A.keras")

#기존 모델에서 출력 층을 제외한 레이어를 가져오기
model_B_on_A = keras.models.Sequential(model_A.layers[:-1])

#출력 층 추가
model_B_on_A.add(keras.layers.Dense(1, activation='sigmoid'))

#모든 레이어가 다시 훈련하지 않도록 설정
for layer in model_B_on_A.layers[:-1]:
    layer.trainable = False
    
model_B_on_A.compile(loss = "binary_crossentropy",
               optimizer=keras.optimizers.SGD(learning_rate=0.001),
               metrics=['accuracy'])
history = model_B_on_A.fit(X_train_B, y_train_B, epochs=20,
                     validation_data=(X_valid_B, y_valid_B))

 

# 모든 레이어가 다시 훈련하도록 설정

#기존 모델에서 출력 층을 제외한 레이어를 가져오기
model_B_on_A = keras.models.Sequential(model_A.layers[:-1])
#출력 층 추가
model_B_on_A.add(keras.layers.Dense(1, activation='sigmoid'))

#모든 레이어가 다시 훈련하도록 설정
for layer in model_B_on_A.layers[:-1]:
    layer.trainable = True
    
model_B_on_A.compile(loss = "binary_crossentropy",
               optimizer=keras.optimizers.SGD(learning_rate=0.001),
               metrics=['accuracy'])
history = model_B_on_A.fit(X_train_B, y_train_B, epochs=20,
                     validation_data=(X_valid_B, y_valid_B))

 

 

# 일반화 오차 비교

print(model_B.evaluate(X_test_B, y_test_B))
print(model_B_on_A.evaluate(X_test_B, y_test_B))

 

 

 

- 고속 옵티마이저 사용

  • 모멘텀 최적화: SGD를 사용할 때 momentum 이라는 파라미터에 값을 설정하면 성능이 좋아짐
  • 모멘텀 최적화에서 훈련 속도를 향상시키고자 할 때는 네스테로프 가속 경사를 이용하게 되는데 이 경우에는 use_nestrov=True를 추가
  • Adagrad, RMSProp, Adam, Nadam 등의 옵티마이저가 추가

 

- 완전 연결층에 규제를 가하는 방법

  • l1 이나 l2 규제를 추가
  • kernel_regularizer 파라미터에 keras.regularizers.l1 이나 l2(값)

 

- Dropout

  • 입력 뉴런은 그대로 사용하고 출력 뉴런은 임시적으로 Drop 될 확률을 설정
    • 이전 레이어에서 전달되는 데이터를 일부분 제거하는 방식
  • 확률은 일반적으로 10 ~ 50% 사이로 설정하는데 RNN에서는 20~30 정도로 설정하고 CNN에서는 40~50% 로 설정
  • 이를 사용하고자 할 때는 Dropout 이라는 클래스에 rate로 확률을 설정
    • 입력 층 앞에는 넣으면 안되고 입력층 다음부터 추가해서 사용

'Python' 카테고리의 다른 글

[Python] 딥러닝 _ Tensorflow  (0) 2024.03.20
[Python] 연관 분석  (0) 2024.03.18
[Python] 연관분석 실습 _ 네이버 지식인 크롤링  (4) 2024.03.15
[Python] 감성 분석 실습  (3) 2024.03.14
[Python] 차원 축소  (0) 2024.03.12

Keras 의 내장 데이터 세트

- 종류

  • boston housing: 보스톤 집값으로 회귀에 사용
  • cifar10: 이미지 분류 데이터로 종류가 10가지
  • cifar100: 이미지 분류 데이터로 종류가 100가지
  • mnist: 손 글씨 데이터
  • fashion mnist: 의류 데이터로 클래스가 10가지
  • imdb: 영화 데이터로 분류에 활용
  • reuters: 뉴스 토픽 - 자연어 분류

- load_data 라는 함수를 호출하면 훈련 데이터 튜플 과 테스트 데이터 튜플로 데이터를 리턴
- 각각의 튜플은 피처 와 레이블로 나뉘어져 있음

 

 

 

1. 데이터 가져오기

fashion_mnist = keras.datasets.fashion_mnist
(X_train_full, y_train_full), (X_test, y_test) = fashion_mnist.load_data()

print(X_train_full.shape) #28 * 28 에 해당하는 이미지 6만개
print(X_test.shape)
print(y_test.shape)
(60000, 28, 28)

(10000, 28, 28)

(10000,)

 

X_train_full[0] #0부터 255까지의 값으로 되어 있음
  • 딥러닝은 0 ~ 1 사이의 실수인 경우 학습을 더 잘하는 것으로 알려져 있음
  • 값을 변경하지 않아도 학습은 가능하지만 되도록이면 0 ~ 1 사이의 숫자로 변환해주는 것이 좋습니다.

 

 

 

2. 정규화 및 훈련/검증 데이터 분할

X_valid, X_train = X_train_full[:5000] / 255. , X_train_full[5000:] / 255.
y_valid, y_train = y_train_full[:5000], y_train_full[5000:]
X_test = X_test/255.

 

 

# 이미지 1개 출력

plt.imshow(X_valid[0], cmap='binary')

 

 

# 각 클래스의 실제 레이블을 배열로 생성

class_names = ["T-shirt/top", "Trouser", "Pullover", "Dress", "Coat",
              "Sandal", "Shirt", "Sneaker", "Bag", "Ankle boot"]
print(y_train[0])
print(class_names[y_train[0]])
plt.imshow(X_train[0], cmap='binary')
4
Coat


 

 

 

 

 

3. Flatten Layer

  • 데이터의 차원을 1차원으로 변경해주는 Layer
  • Dense Layer 는 입력받는 데이터의 차원이 1차원만 가능
    • Dense Layer는 입력 데이터의 차원을 1차원으로 변경해서 사용해야 합니다.
    • 이미지 데이터의 경우는 변환을 해서 사용해야 합니다.
  • Flatten Layer를 자동으로 1차원으로 변경을 수행합니다.
print(np.array([[1, 2], [3, 4]]).flatten())
[1 2 3 4]

 

 

 

- 활성화 함수

- ReLU: max(0, x)
- Leaky ReLU: max(x, 0.1x) - 이론적으로 기울기 소실이 일어날 수 있지만 실제로는 거의 일어나지 않음

 

 

 

 

 

4. 모델 생성

model = keras.models.Sequential()
  • Dense는 데이터의 차원을 1차원으로 입력을 받아야 합니다.
  • 이미지 데이터의 경우는 가로 * 세로 또는 가로 * 세로 * 채널의수 로 입력
  • Flatten 은 원본 이미지의 차원을 주면 1차원으로 수정을 합니다.
model.add(keras.layers.Flatten(input_shape=[28, 28])) #Input Layer
model.add(keras.layers.Dense(300, activation="relu")) #Hidden Layer
model.add(keras.layers.Dense(200, activation="relu")) #Hidden Layer
model.add(keras.layers.Dense(100, activation="relu")) #Hidden Layer
  • Output Layer의 units 는 출력의 개수가 되어야 합니다.
  • 분류 문제는 활성화 함수로 softmax 가 잘 동작합니다.
  • 출력을 하나로 만들고자 하면 sigmoid를 사용하면 됩니다.
model.add(keras.layers.Dense(10, activation="softmax")) #Output Layer

 

 

 

 

 

5. 모델 구조 확인

model.summary()

 

 

- 손실 함수

  • 출력층의 활성화 함수가 sigmoid: binary_crossentropy
  • 출력층의 활성화 함수가 softmax: 
    • categorical_crossentropy(원핫인코딩 된 경우)
    • sparse_categorical_crossentropy(원핫인코딩 안된 경우)

 

 

- 옵티마이저

  • 손실을 낮추기 위해서 신경망의 가중치 와 학습률 등의 신경망의 속성을 변경하는데 사용되는 최적화 방식
  • 종류
    • SGD(확률적 경사하강법)
    • RMSprop
    • Adam: 가장 많이 사용되는 알고리즘으로 좋은 성능을 내는 것으로 알려져 있음
    • Adadelta
    • Adagrad
    • Nadam
    • Ftrl

 

- 평가 지표

  • 분류: auc, precision, recall, accuracy
  • 회귀: mse, mae, rmse

 

 

 

 

6. 모델 complie

model.compile(optimizer='adam',
             loss='sparse_categorical_crossentropy', 
             metrics=['accuracy'])

# 모델 훈련
history = model.fit(X_train, y_train, epochs=30,
                   validation_data = (X_valid, y_valid))

 

 

# 일반화 오차 - 평가

model.evaluate(X_test, y_test)

 

 

# 예측

  • predict: 각 클래스에 대한 기댓값
  • predict_classes: 가장 높은 클래스가 리턴
X_new = X_test[:3]
y_proba = model.predict(X_new)
print(y_proba.round(3))

 

y_pred = np.argmax(model.predict(X_new), axis=-1)
print(y_pred)
print(np.array(class_names)[y_pred])