데이터에는 여러 종류가 있죠? 이미지를 떠올리시는 분도, 텍스트를 떠올리시는 분도 계실텐데요, 이번에는 CSV 파일 형식의 데이터를 가져오는 법에 대해 알아보겠습니다.
https://www.tensorflow.org/beta/tutorials/load_data/csv
아래 설명은 Tensorflow 2.0 Tutorial 을 참고하였음을 밝힙니다.
To see the English version of this story, click (here).
I. 들어가며
데이터 가져오기??
데이터를 가져오는 것은 코딩의 첫걸음입니다. 여러분, 김치 없이 김치볶음밥을 만드실 수 있나요? 굉장히 어이 없는 질문일텐데, 코딩을 할 때도 학습하고 예측할 재료가 꼭 필요합니다. 데이터는 코딩의 핵심 재료, 데이터 가져오기는 코딩 재료 준비라고 볼 수 있습니다.
텐서플로우 튜토리얼을 활용해 텐서플로우를 배우고 연습할 때는 구글이 잘 만들어놓은 자료를 쉽게 이용만 하면 되지만, 진짜로 실제 데이터를 직접 분석할 때는 학습시키기 이전에 데이터를 다듬어야합니다. 김치볶음밥을 만들 때 양파를 씻고 껍질도 벗기고 칼로 썰어서 후라이팬에 넣는 것처럼, 학습에 쓸 수 있도록 데이터셋도 알맞게 손질해줘야해요.
지금부터, 떠먹여주는 코딩 말고! 같이 차근차근 코딩을 만들어 먹어볼까요?
CSV 파일이란?
CSV 파일은 간단하게 말해서 엑셀 파일의 한 종류인데요, 프로그래밍 환경에 데이터를 가져오고 처리하기 편하게 쉼표로 항목을 구분한 엑셀 파일입니다.
왜 굳이?
CSV 파일은 전세계적으로 데이터 저장에 많이 쓰이는 형식입니다. 많은 정부와 회사가 CSV 파일로 데이터를 제공하기 때문에 ($$$파이썬의 시대에도 두둑해지는 빌게이츠의 지갑$$$), 알아두면 정말 유용합니다.
+) 새로운 텐서플로우는 pandas를 활용하지 않아도 csv 파일을 바로 불러올 수 있게 업그레이드 되었습니다.
그럼 이제, 본격적으로 시작해봅시다!
II. 준비하기
- 기술적인 준비
try:
# %tensorflow_version only exists in Colab.
%tensorflow_version 2.x
except Exception:
pass
Try와 except를 활용하여 텐서플로우를 설치하는 코드입니다. **코랩 환경에서만 작동하는 코드입니다**
+) Try, except, %의 기능에 대해 더 알고 싶은 분은 이 스토리를 참조하세요:
[Python] Try and Except, %magic functions
텐서플로우 2.x 시작해 보기
medium.com
from __future__ import absolute_import, division, print_function,from __future__ import absolute_import, division, print_function, unicode_literals
import functoolsimport numpy as np
import tensorflow as tf
이 코드 역시 numpy와 텐서플로우를 가져오는 주문입니다.
+) from __future__ import absolute_import, division, print_function, unicode_literals
에 대해 더 알아보고 싶으신 분들은 이 링크를 참조하세요;
[Python] from __future__ import
텐서플로우 2.x 시작해보기
medium.com
이제 텐서플로우 설치가 완료되었습니다. 그럼 우리가 분석할 데이터셋을 한 번 살펴볼까요?
2. 데이터셋 살펴보기
우리가 학습시킬 데이터셋에는 627명의 타이타닉 호 승객의 정보(성별, 나이, 좌석 등급 등)가 들어있습니다.
몇 개 column이 무엇을 의미하는지 자세히 살펴볼까요?
- Survived: 생존 (1), 사망(0)
- n_siblings_spouses: 함께 탑승한 배우자나 형제의 수
- parch: 함께 탑승한 부모나 자녀의 수
- embark_town: 승선 장소(얼마나 부유한지 파악할 수 있음)
마지막에, 우리는 column들이 나타내는 특성이 타이타닉 호 침몰 사고에서 생존율과 얼마나 관련있는지 예측할 것입니다.
그러면 이제, 살펴본 데이터셋을 CSV 파일 형식으로 다운받아 볼까요?
3. URL로 CSV 파일 다운받기
아래의 코드를 실행하면, 학습 데이터셋과 테스트 데이터셋을 다운 받을 수 있습니다. 앞서 말씀드렸던 ‘코딩 재료 준비’ 과정입니다
TRAIN_DATA_URL = "https://storage.googleapis.com/tf-datasets/titanic/train.csv"
TEST_DATA_URL = "https://storage.googleapis.com/tf-datasets/titanic/eval.csv"
train_file_path = tf.keras.utils.get_file("train.csv", TRAIN_DATA_URL)
test_file_path = tf.keras.utils.get_file("eval.csv", TEST_DATA_URL)# Make numpy values easier to read.
np.set_printoptions(precision=3, suppress=True)
III. 데이터 가져오기
위의 코드를 통해 제 컴퓨터에 CSV 파일을 직접 다운 받아서 여러분께 캡쳐 화면으로 보여드린건데, 가끔 실제 CSV파일을 컴퓨터에 다운 받기 힘든 경우가 있습니다. 그 때, 우리는 하단의 코드를 활용하여 CSV파일의 윗부분을 확인할 수 있습니다.
!head {train_file_path}
결과는 학습 데이터 캡쳐 화면과 같습니다.
우리는 생존율에 관심이 있기 때문에, ‘survived’ column을 많이 보게 될 건데요, 나중에 다시 보기 쉽도록 ‘survived’ column을 LABEL_COLUMN으로 지정하고, column의 값 0, 1을 LABELS 값으로 지정해보겠습니다.
LABEL_COLUMN = 'survived'
LABELS = [0, 1]
이제 재료 준비가 끝난 것 같네요! 데이터를 읽고 데이터셋을 만들어볼까요?
def get_dataset(file_path, **kwargs):
dataset = tf.data.experimental.make_csv_dataset(
file_path,
batch_size=5, # Artificially small to make examples easier to show.
label_name=LABEL_COLUMN,
na_value="?",
num_epochs=1,
ignore_errors=True,
**kwargs)
return dataset
우선 get_dataset 함수를 정의하겠습니다. 이 함수는 인풋값으로 file_path를 갖습니다. 참고로 우리는 train_file_path와 test_file_path, 이 두가지 file path를 가지고 있습니다.
**kwargs(‘keyword argument’의 줄임말)는 나중에 argument를 추가하고 싶을 때, 추가할 수 있도록 해주는 보호 장치입니다.
tf.data.experimental.make_csv_dataset
에 대해 자세히 알고 싶으신 분들은 하단의 링크를 참조하세요:
tf.data.experimental.make_csv_dataset | TensorFlow Core r2.0
Join us at TensorFlow World, Oct 28–31. Use code TF20 for 20% off select passes. Register now Reads CSV files into a…
www.tensorflow.org
**안내** 텐서플로우 2.0은 베타버전으로, 수정될 수 있습니다.
tf.data.experimental.make_csv_dataset
의 작동원리와, 각 argument들의 의미에 대해 알아보겠습니다.
- 기본 작동원리
tf.data.experimental.make_csv_dataset
이 코드는, CSV파일을 튜플 형식으로 생긴 batch 데이터셋으로 만듭니다. 그리고 features를 담은 딕셔너리는 ‘텐서’에 column이름을 보여줍니다.
여기서 잠깐, ‘텐서’는 무엇일까요? 감이 잘 안오는데요… 쉽게 말하면 ‘도라에몽 주머니’ 정도가 될 것 같습니다. 도라에몽 주머니에는 크기와 종류 상관 없이 정말정말 많은 도구와 물건이 들어갑니다. 이런 4차원 주머니처럼 텐서도 모든 것을 안에 담을 수 있습니다. 텐서의 크기, 또는 텐서가 포함하는 데이터의 크기는 “rank”라고 불립니다.
- 텐서가 스칼라를 포함할 때, rank=1
- 한 줄이나 기둥의 벡터, rank=1
- 모든 n X m 크기행렬, rank=2.
- 그 이상 3차원 데이터는 3 이상의 rank.
텐서는 데이터의 사이즈에 맞춰서 늘어나기 때문에 도라에몽 주머니처럼 어떤 사이즈든 넣을 수 있고, 언제든 원할 때 데이터를 불러올 수 있습니다.
2. arguments
- file_path: CSV 기록을 포함하는 file path들의 리스트
- batch_size: batch 하나에 몇개의 기록을 넣을지 정하는 숫자. 위 같은 경우 한 batch에 CSV 5줄씩이 들어감.
- label_name: LABEL_COLUMN 에 label name ‘predicted’를 할당해 줌.
- na_value: NA/NAN인 string이 나오면 “?”가 나오게 할당해 줌.
- num_epochs: 데이터셋이 반복되는 횟수.
raw_train_data = get_dataset(train_file_path)
raw_test_data = get_dataset(test_file_path)
학습시킬 데이터셋과 테스트 데이터셋을 get_dataset함수에 집어넣어 raw_train_data와 raw_test_data를 만들었습니다.
어떻게 생겼는지 궁금하니까 show_batch함수를 정의하고, raw_train_data가 어떻게 생겼는지 알아보겠습니다.
def show_batch(dataset):
for batch, label in dataset.take(1):
for key, value in batch.items():
print("{:20s}: {}".format(key,value.numpy()))show_batch(raw_train_data)
sex : [b’female’ b’female’ b’male’ b’female’ b’male’]
age : [51. 28. 31. 28. 43.]
n_siblings_spouses : [1 0 0 1 0]
parch : [0 0 0 0 0]
fare : [77.958 79.2 7.775 15.5 8.05 ]
class : [b’First’ b’First’ b’Third’ b’Third’ b’Third’]
deck : [b’D’ b’unknown’ b’unknown’ b’unknown’ b’unknown’]
embark_town : [b’Southampton’ b’Cherbourg’ b’Southampton’ b’Queenstown’ b’Southampton’]
alone : [b’n’ b’y’ b’y’ b’n’ b’y’]
텐서플로우가 자동으로 column name들을 읽었는데, 몇 가지 다른 경우도 살펴보자면:
- 여러분의 파일에 column name이 첫 줄에 없거나 아예 없을 때, column name들을 만들고, tf.data.experimental.make_csv_dataset에 column_names argument를 column name을 추가로 넣어주시면 됩니다.
CSV_COLUMNS = ['survived', 'sex', 'age', 'n_siblings_spouses', 'parch', 'fare', 'class', 'deck', 'embark_town', 'alone']temp_dataset = get_dataset(train_file_path, column_names=CSV_COLUMNS)show_batch(temp_dataset)
sex : [b’male’ b’male’ b’female’ b’male’ b’male’]
age : [26. 34. 28. 21. 45.]
n_siblings_spouses : [0 1 2 0 0] …
alone : [b’y’ b’n’ b’n’ b’n’ b’y’]
2. 몇 개의 특정 column 데이터만 사용하고 싶으면, 1번 항목과 비슷하게 여러분이 관심있는 column name만 담아 리스트를 만들고 select_columns argument를 활용해 넣어주세요.
SELECT_COLUMNS = ['survived', 'age', 'n_siblings_spouses', 'class', 'deck', 'alone']temp_dataset = get_dataset(train_file_path, select_columns=SELECT_COLUMNS)show_batch(temp_dataset)
age : [28. 28. 25. 25. 19.]
n_siblings_spouses : [0 0 1 0 0]
class : [b’Third’ b’Third’ b’First’ b’Third’ b’Third’]
deck : [b’unknown’ b’unknown’ b’B’ b’F’ b’unknown’]
alone : [b’y’ b’n’ b’n’ b’y’ b’y’]
IV. 데이터 처리하기
지금까지 우리는 CSV파일을 불러오고 텐서들에 그 데이터를 저장했는데, 양파, 스팸(참치 또는 햄), 김치와 밥을 준비했다고 볼 수 있습니다. 이제 재료를 볶기 알맞게 같은 크기로 썰어줄 차례인데, 데이터 사이언스에서는 이 과정을 데이터 전처리라고 합니다.
우리가 할 것을 정리해보면
- 데이터를 종류에 따라 continuous data 와 categorical data 로 분류
- 용이한 모델링을 위해 데이터를 똑같은 길이의 벡터로 변환
- 모델링을 위해 두 column data 병합
A. Continuous Data
i) 우리는 continuous data 타입인 column 5개가 있으므로, 그 5개만 선택해보겠습니다: 'survived', 'age', 'n_siblings_spouses', 'parch', 'fare'
SELECT_COLUMNS = ['survived', 'age', 'n_siblings_spouses', 'parch', 'fare']
DEFAULTS = [0, 0.0, 0.0, 0.0, 0.0]
temp_dataset = get_dataset(train_file_path,
select_columns=SELECT_COLUMNS,
column_defaults = DEFAULTS)show_batch(temp_dataset)
age : [24. 18. 60. 28. 28.]
n_siblings_spouses : [1. 1. 1. 0. 0.]
parch : [2. 0. 1. 0. 0.]
fare : [65. 17.8 79.2 7.75 26.55]
ii) column들을 tf.stack 함수를 통해 packing해보겠습니다. 다시 말해, pack(features, label).
def pack(features, label):
return tf.stack(list(features.values()), axis=-1), label
그리고, 데이터셋에 pack() 을 적용해보겠습니다.
packed_dataset = temp_dataset.map(pack)for features, labels in packed_dataset.take(1):
print(features.numpy())
print()
print(labels.numpy())
[[24. 0. 0. 13. ] [65. 0. 0. 26.55] [22. 0. 0. 7.25] [37. 1. 0. 26. ] [60. 1. 1. 79.2 ]]
[0 0 0 0 1] <-labels (pack()함수를 정의할때 labels를 따로 프린트하도록 정의했던 것을 확인해보세요)
하단의 결과와 상단의 결과를 비교해보세요.
age : [24. 18. 60. 28. 28.]
n_siblings_spouses : [1. 1. 1. 0. 0.]
parch : [2. 0. 1. 0. 0.]
fare : [65. 17.8 79.2 7.75 26.55]
데이터를 5개의 항목을 가진 리스트로 정리해보았는데, 예를 들어 하나를 살펴보겠습니다. [60. 1. 1. 79.2 ]. 이 사람은 60살이고, 배우자나 형제 한 명, 그리고 부모나 자녀 한명과 탑승했고, 79.2 달러를 지불했고, 생존했습니다!
iii) 지금까지 numeric data를 뽑아내서 column데이터 하나로 만들었고, 이제 numeric data를 골라 single column으로 전처리를 해보겠습니다.
example_batch, labels_batch = next(iter(temp_dataset))class PackNumericFeatures(object):
def __init__(self, names):
self.names = namesdef __call__(self, features, labels):
numeric_freatures = [features.pop(name) for name in self.names]
numeric_features = [tf.cast(feat, tf.float32) for feat in numeric_freatures]
numeric_features = tf.stack(numeric_features, axis=-1)
features['numeric'] = numeric_featuresreturn features, labelsNUMERIC_FEATURES = ['age','n_siblings_spouses','parch', 'fare']packed_train_data = raw_train_data.map(
PackNumericFeatures(NUMERIC_FEATURES))packed_test_data = raw_test_data.map(
PackNumericFeatures(NUMERIC_FEATURES))show_batch(packed_train_data)
sex : [b’male’ b’female’ b’male’ b’male’ b’male’]
class : [b’Third’ b’Third’ b’First’ b’Third’ b’First’]
deck : [b’unknown’ b’unknown’ b’C’ b’unknown’ b’C’]
embark_town : [b’Southampton’ b’Southampton’ b’Southampton’ b’Southampton’ b’Cherbourg’]
alone : [b’n’ b’n’ b’n’ b’y’ b’n’]
numeric : [[ 1. 1. 2. 20.575] [ 41. 0. 2. 20.212] [ 64. 1. 4. 263. ] [ 18. 0. 0. 7.75 ] [ 49. 1. 0. 89.104]]
iv) continuous data 정규화 (정규분포를 가정하는것이 아님에 유의하세요)
‘age’ : 0.75 이상 80 이하
‘n_sibilings_spouses’ : 0 이상 8 이하
‘parch’ : 0 이상 5 이하
‘fare’ : 0 이상 512 이하
import pandas as pd
desc = pd.read_csv(train_file_path)[NUMERIC_FEATURES].describe()
desc
MEAN = np.array(desc.T['mean'])
STD = np.array(desc.T['std'])def normalize_numeric_data(data, mean, std):
# Center the data
return (data-mean)/std
이제 정규화된 numeric column을 만들어보겠습니다.
먼저, functolls.partial을 사용해 normalize_numeric data와 MEAN, STD를 묶어보겠습니다.
normalizer = functools.partial(normalize_numeric_data, mean=MEAN, std=STD)
정규화 기능을 tf.feature_columns.numeric_column를 사용해 각각의 batch에 적용해보겠습니다.
numeric_column = tf.feature_column.numeric_column('numeric', normalizer_fn=normalizer, shape=[len(NUMERIC_FEATURES)])
numeric_columns = [numeric_column]
numeric_column
아래 결과값을 통해 iv) continuous data 정규화에서 우리가 어떤 걸 했는지 살펴볼 수 있습니다.
example_batch['numeric']
array([[ 21. , 2. , 2. , 262.375],
[ 39. , 0. , 0. , 26. ],
[ 28. , 0. , 0. , 7.729],
[ 37. , 2. , 0. , 7.925],
[ 20. , 0. , 0. , 9.225]], dtype=float32)>
numeric_layer = tf.keras.layers.DenseFeatures(numeric_columns)
numeric_layer(example_batch).numpy()
array([[-0.69 , 1.264, 2.043, 4.176],
[ 0.749, -0.474, -0.479, -0.154],
[-0.13 , -0.474, -0.479, -0.488],
[ 0.589, 1.264, -0.479, -0.485],
[-0.77 , -0.474, -0.479, -0.461]], dtype=float32)
B. Categorical Data
이제 5개의 column들이 남았고, ‘sex’, ‘class’, ‘deck’, ‘embark_town’, ‘alone’ 이 column들은 categorical column들입니다.and these are categorical columns. CATEGORIES 에서 categorical 변수의 이름을 정해주고, 가능한 결과값을 할당해주겠습니다. (ex. ‘sex’ : [‘male’, ‘female’])
CATEGORIES = {
'sex': ['male', 'female'],
'class' : ['First', 'Second', 'Third'],
'deck' : ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'],
'embark_town' : ['Cherbourg', 'Southhampton', 'Queenstown'],
'alone' : ['y', 'n']
}
각 categorical column에 tf.feature_column.indicator_column 을적용해보겠습니다.
categorical_columns = []
for feature, vocab in CATEGORIES.items():
cat_col = tf.feature_column.categorical_column_with_vocabulary_list(
key=feature, vocabulary_list=vocab)
categorical_columns.append(tf.feature_column.indicator_column(cat_col))# See what you just created.
categorical_columns[IndicatorColumn(categorical_column=VocabularyListCategoricalColumn(key='sex', vocabulary_list=('male', 'female'), dtype=tf.string, default_value=-1, num_oov_buckets=0)),
IndicatorColumn(categorical_column=VocabularyListCategoricalColumn(key='alone', vocabulary_list=('y', 'n'), dtype=tf.string, default_value=-1, num_oov_buckets=0)),
IndicatorColumn(categorical_column=VocabularyListCategoricalColumn(key='deck', vocabulary_list=('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'), dtype=tf.string, default_value=-1, num_oov_buckets=0)),
IndicatorColumn(categorical_column=VocabularyListCategoricalColumn(key='embark_town', vocabulary_list=('Cherbourg', 'Southhampton', 'Queenstown'), dtype=tf.string, default_value=-1, num_oov_buckets=0)),
IndicatorColumn(categorical_column=VocabularyListCategoricalColumn(key='class', vocabulary_list=('First', 'Second', 'Third'), dtype=tf.string, default_value=-1, num_oov_buckets=0))]
이제 numerical data에 마지막에 적용했던 것을 다시 적용해보겠습니다. Numerical data의 경우, 아래와 같은 코드를 적용했는데요:
numeric_layer = tf.keras.layers.DenseFeatures(numeric_columns)
numeric_layer(example_batch).numpy()
Categorical data의 경우,
categorical_layer = tf.keras.layers.DenseFeatures(categorical_columns)
print(categorical_layer(example_batch).numpy()[0])
결과는:
[0. 1. 1. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 1.]
C. 레이어 전처리 병합
지금까지 모든 재료를 알맞은 크기로 똑같이 썰었는데요, 이제 팬에 한꺼번에 넣어보겠습니다.
preprocessing_layer = tf.keras.layers.DenseFeatures(categorical_columns+numeric_columns)print(preprocessing_layer(example_batch).numpy()[0])
[ 0. 1. 1. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. -0.69 1.264 2.043 4.176 0. 1. ]
V. 모델 만들기
이제 거의 막바지로 접어들었는데요, 지금까지 열심히 씻고, 알맞은 크기로 썰어놓은 재료들을 볶아 김치볶음밥을 완성할 시간입니다! 학습시킬 모델을 만들기부터 예측까지, 프로그래밍의 꽃을 만나보실까요?
우선 레이어를 전처리하고, 모델을 만들어보겠습니다.
model = tf.keras.Sequential([
preprocessing_layer,
tf.keras.layers.Dense(128, activation='relu'),
tf.keras.layers.Dense(128, activation='relu'),
tf.keras.layers.Dense(1, activation='sigmoid'),
])model.compile(
loss='binary_crossentropy',
optimizer='adam',
metrics=['accuracy'])
II. 준비하기 부터 V. 모델만들기까지 레이어 전처리를 했는데요, 모델링의 핵심은 데이터를 잘 다듬고, 필요한 걸 잘 선택하는 것입니다.
VI. 학습시켜서 테스트해보고, 예측하기
- 학습시키기
train_data = packed_train_data.shuffle(500)
test_data = packed_test_datamodel.fit(train_data, epochs=20)
2. 테스트 데이터셋에서 정확도 테스트하기
test_loss, test_accuracy = model.evaluate(test_data)print('\n\nTest Loss {}, Test Accuracy {}'.format(test_loss, test_accuracy))
Test Loss 0.47088844026878196, Test Accuracy 0.8068181872367859
3. 예측하기
주어진 정보로 생존율을 예측해보겠습니다.
predictions = model.predict(test_data)# Show some results
for prediction, survived in zip(predictions[:10], list(test_data)[0][1][:10]):
print("Predicted survival: {:.2%}".format(prediction[0]),
" | Actual outcome: ",
("SURVIVED" if bool(survived) else "DIED"))
예상 생존률: 90.76% | 실제 결과: 생존
예상 생존률: 26.12% | 실제 결과: 사망
예상 생존률: 20.50% | 실제 결과: 사망
예상 생존률: 14.20% | 실제 결과: 사망
예상 생존률: 11.52% | 실제 결과: 사망
지금까지 CSV파일로 데이터를 불러와보았는데요, 도움이 좀 되셨나요?
궁금하신 점이나 개선할 점이 있으시면 언제든 댓글로 해주시고,
CLAP은 항상 큰 힘이 됩니다.
읽어주셔서 감사합니다.