[Tensorflow 2.0] CSV 파일 형식 데이터 가져오기

A Ydobon
26 min readSep 24, 2019

--

데이터에는 여러 종류가 있죠? 이미지를 떠올리시는 분도, 텍스트를 떠올리시는 분도 계실텐데요, 이번에는 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. 준비하기

  1. 기술적인 준비
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들의 의미에 대해 알아보겠습니다.

  1. 기본 작동원리

tf.data.experimental.make_csv_dataset 이 코드는, CSV파일을 튜플 형식으로 생긴 batch 데이터셋으로 만듭니다. 그리고 features를 담은 딕셔너리는 ‘텐서’에 column이름을 보여줍니다.

여기서 잠깐, ‘텐서’는 무엇일까요? 감이 잘 안오는데요… 쉽게 말하면 ‘도라에몽 주머니’ 정도가 될 것 같습니다. 도라에몽 주머니에는 크기와 종류 상관 없이 정말정말 많은 도구와 물건이 들어갑니다. 이런 4차원 주머니처럼 텐서도 모든 것을 안에 담을 수 있습니다. 텐서의 크기, 또는 텐서가 포함하는 데이터의 크기는 “rank”라고 불립니다.

  • 텐서가 스칼라를 포함할 때, rank=1
  • 한 줄이나 기둥의 벡터, rank=1
  • 모든 n X m 크기행렬, rank=2.
  • 그 이상 3차원 데이터는 3 이상의 rank.
출처 https://www.mystalk.net/detail/1753721513918152382_6219722394/

텐서는 데이터의 사이즈에 맞춰서 늘어나기 때문에 도라에몽 주머니처럼 어떤 사이즈든 넣을 수 있고, 언제든 원할 때 데이터를 불러올 수 있습니다.

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들을 읽었는데, 몇 가지 다른 경우도 살펴보자면:

  1. 여러분의 파일에 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파일을 불러오고 텐서들에 그 데이터를 저장했는데, 양파, 스팸(참치 또는 햄), 김치와 밥을 준비했다고 볼 수 있습니다. 이제 재료를 볶기 알맞게 같은 크기로 썰어줄 차례인데, 데이터 사이언스에서는 이 과정을 데이터 전처리라고 합니다.

우리가 할 것을 정리해보면

  1. 데이터를 종류에 따라 continuous data 와 categorical data 로 분류
  2. 용이한 모델링을 위해 데이터를 똑같은 길이의 벡터로 변환
  3. 모델링을 위해 두 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. 학습시켜서 테스트해보고, 예측하기

  1. 학습시키기
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은 항상 큰 힘이 됩니다.

읽어주셔서 감사합니다.

--

--

A Ydobon
A Ydobon

No responses yet