หน้านี้ได้รับการแปลโดย Cloud Translation API
Switch to English

โหลดข้อมูล CSV

ดูใน TensorFlow.org เรียกใช้ใน Google Colab ดูแหล่งที่มาบน GitHub ดาวน์โหลดสมุดบันทึก

บทช่วยสอนนี้แสดงตัวอย่างวิธีการใช้ข้อมูล CSV กับ TensorFlow

มีสองส่วนหลักดังนี้:

  1. กำลังโหลดข้อมูลออกจากดิสก์
  2. ก่อนประมวลผลเป็นรูปแบบที่เหมาะสมสำหรับการฝึกอบรม

บทช่วยสอนนี้มุ่งเน้นไปที่การโหลดและให้ตัวอย่างสั้น ๆ ของการประมวลผลล่วงหน้า สำหรับบทช่วยสอนที่เน้นด้านการประมวลผลก่อนการประมวลผลโปรดดู คำแนะนำ และ บทแนะนำเกี่ยว กับ เลเยอร์ก่อน การ ประมวลผล

ติดตั้ง

import pandas as pd
import numpy as np

# Make numpy values easier to read.
np.set_printoptions(precision=3, suppress=True)

import tensorflow as tf
from tensorflow.keras import layers
from tensorflow.keras.layers.experimental import preprocessing

ในข้อมูลหน่วยความจำ

สำหรับชุดข้อมูล CSV ขนาดเล็กวิธีที่ง่ายที่สุดในการฝึกโมเดล TensorFlow คือการโหลดลงในหน่วยความจำเป็น Dataframe แพนด้าหรืออาร์เรย์ NumPy

ตัวอย่างที่ค่อนข้างง่ายคือ ชุดข้อมูลหอยเป๋าฮื้อ

  • ชุดข้อมูลมีขนาดเล็ก
  • คุณสมบัติการป้อนข้อมูลทั้งหมดเป็นค่าทศนิยมแบบ จำกัด ช่วง

วิธีดาวน์โหลดข้อมูลลงใน Pandas DataFrame ดังนี้

abalone_train = pd.read_csv(
    "https://storage.googleapis.com/download.tensorflow.org/data/abalone_train.csv", header=None, 
    names=["Length", "Diameter", "Height", "Whole weight", "Shucked weight",
           "Viscera weight", "Shell weight", "Age"])

abalone_train.head()

ชุดข้อมูลประกอบด้วยชุดการวัด หอยเป๋าฮื้อ หอยทากทะเลชนิดหนึ่ง

เปลือกหอยเป๋าฮื้อ

“ หอยเป๋าฮื้อ” (โดย Nicki Dugan Pogue , CC BY-SA 2.0)

งานเล็กน้อยสำหรับชุดข้อมูลนี้คือการคาดเดาอายุจากการวัดอื่น ๆ ดังนั้นให้แยกคุณสมบัติและป้ายกำกับสำหรับการฝึกอบรม:

abalone_features = abalone_train.copy()
abalone_labels = abalone_features.pop('Age')

สำหรับชุดข้อมูลนี้คุณจะปฏิบัติต่อคุณลักษณะทั้งหมดเหมือนกัน รวมคุณสมบัติไว้ในอาร์เรย์ NumPy เดียว:

abalone_features = np.array(abalone_features)
abalone_features
array([[0.435, 0.335, 0.11 , ..., 0.136, 0.077, 0.097],
       [0.585, 0.45 , 0.125, ..., 0.354, 0.207, 0.225],
       [0.655, 0.51 , 0.16 , ..., 0.396, 0.282, 0.37 ],
       ...,
       [0.53 , 0.42 , 0.13 , ..., 0.374, 0.167, 0.249],
       [0.395, 0.315, 0.105, ..., 0.118, 0.091, 0.119],
       [0.45 , 0.355, 0.12 , ..., 0.115, 0.067, 0.16 ]])

จากนั้นสร้างแบบจำลองการถดถอยทำนายอายุ เนื่องจาก keras.Sequential เซอร์อินพุตเพียงตัวเดียวจึงมี keras.Sequential แบบจำลองตามลำดับก็เพียงพอแล้วที่นี่

abalone_model = tf.keras.Sequential([
  layers.Dense(64),
  layers.Dense(1)
])

abalone_model.compile(loss = tf.losses.MeanSquaredError(),
                      optimizer = tf.optimizers.Adam())

ในการฝึกโมเดลนั้นให้ส่งผ่านคุณสมบัติและป้ายกำกับไปที่ Model.fit :

abalone_model.fit(abalone_features, abalone_labels, epochs=10)
Epoch 1/10
104/104 [==============================] - 0s 2ms/step - loss: 62.3697
Epoch 2/10
104/104 [==============================] - 0s 1ms/step - loss: 11.7374
Epoch 3/10
104/104 [==============================] - 0s 1ms/step - loss: 8.6329
Epoch 4/10
104/104 [==============================] - 0s 1ms/step - loss: 8.1600
Epoch 5/10
104/104 [==============================] - 0s 2ms/step - loss: 7.7406
Epoch 6/10
104/104 [==============================] - 0s 2ms/step - loss: 7.4095
Epoch 7/10
104/104 [==============================] - 0s 2ms/step - loss: 7.1435
Epoch 8/10
104/104 [==============================] - 0s 1ms/step - loss: 6.9596
Epoch 9/10
104/104 [==============================] - 0s 1ms/step - loss: 6.8104
Epoch 10/10
104/104 [==============================] - 0s 1ms/step - loss: 6.7002

<tensorflow.python.keras.callbacks.History at 0x7f57c40b6fd0>

คุณเพิ่งเห็นวิธีพื้นฐานที่สุดในการฝึกโมเดลโดยใช้ข้อมูล CSV จากนั้นคุณจะได้เรียนรู้วิธีใช้การประมวลผลล่วงหน้าเพื่อทำให้คอลัมน์ตัวเลขเป็นปกติ

การประมวลผลขั้นพื้นฐาน

เป็นแนวทางปฏิบัติที่ดีในการปรับอินพุตให้กับโมเดลของคุณเป็นปกติ เลเยอร์ experimental.preprocessing เป็นวิธีที่สะดวกในการสร้าง Normalization นี้ในโมเดลของคุณ

เลเยอร์จะคำนวณค่าเฉลี่ยและความแปรปรวนของแต่ละคอลัมน์ไว้ล่วงหน้าและใช้สิ่งเหล่านี้เพื่อทำให้ข้อมูลเป็นปกติ

ก่อนอื่นคุณต้องสร้างเลเยอร์:

normalize = preprocessing.Normalization()

จากนั้นคุณใช้เมธอด Normalization.adapt() เพื่อปรับเลเยอร์การทำให้เป็นมาตรฐานกับข้อมูลของคุณ

normalize.adapt(abalone_features)

จากนั้นใช้เลเยอร์นอร์มัลไลเซชันในโมเดลของคุณ:

norm_abalone_model = tf.keras.Sequential([
  normalize,
  layers.Dense(64),
  layers.Dense(1)
])

norm_abalone_model.compile(loss = tf.losses.MeanSquaredError(),
                           optimizer = tf.optimizers.Adam())

norm_abalone_model.fit(abalone_features, abalone_labels, epochs=10)
Epoch 1/10
104/104 [==============================] - 0s 2ms/step - loss: 93.5696
Epoch 2/10
104/104 [==============================] - 0s 2ms/step - loss: 54.1816
Epoch 3/10
104/104 [==============================] - 0s 2ms/step - loss: 16.3253
Epoch 4/10
104/104 [==============================] - 0s 2ms/step - loss: 5.8225
Epoch 5/10
104/104 [==============================] - 0s 2ms/step - loss: 5.0510
Epoch 6/10
104/104 [==============================] - 0s 2ms/step - loss: 4.9794
Epoch 7/10
104/104 [==============================] - 0s 2ms/step - loss: 4.9541
Epoch 8/10
104/104 [==============================] - 0s 2ms/step - loss: 4.9453
Epoch 9/10
104/104 [==============================] - 0s 2ms/step - loss: 4.9258
Epoch 10/10
104/104 [==============================] - 0s 2ms/step - loss: 4.9344

<tensorflow.python.keras.callbacks.History at 0x7f57b8031668>

ชนิดข้อมูลผสม

ชุดข้อมูล "ไททานิก" มีข้อมูลเกี่ยวกับผู้โดยสารบนเรือไททานิก งานเล็กน้อยในชุดข้อมูลนี้คือการทำนายว่าใครรอดชีวิต

ไททานิค

ภาพ จาก Wikimedia

ข้อมูลดิบสามารถโหลดเป็น Pandas DataFrame ได้อย่างง่ายดาย แต่ไม่สามารถใช้เป็นอินพุตไปยังโมเดล TensorFlow ได้ทันที

titanic = pd.read_csv("https://storage.googleapis.com/tf-datasets/titanic/train.csv")
titanic.head()
titanic_features = titanic.copy()
titanic_labels = titanic_features.pop('survived')

เนื่องจากประเภทข้อมูลและช่วงที่แตกต่างกันคุณจึงไม่สามารถซ้อนคุณสมบัติลงในอาร์เรย์ NumPy และส่งต่อไปยัง keras.Sequential ได้ แต่ละคอลัมน์จำเป็นต้องจัดการแยกกัน

ในฐานะทางเลือกหนึ่งคุณสามารถประมวลผลข้อมูลล่วงหน้าแบบออฟไลน์ (โดยใช้เครื่องมือใดก็ได้ที่คุณต้องการ) เพื่อแปลงคอลัมน์หมวดหมู่เป็นคอลัมน์ตัวเลขจากนั้นส่งเอาต์พุตที่ประมวลผลไปยังโมเดล TensorFlow ของคุณ ข้อเสียของแนวทางดังกล่าวคือถ้าคุณบันทึกและส่งออกโมเดลของคุณการประมวลผลล่วงหน้าจะไม่ถูกบันทึกด้วย เลเยอร์ experimental.preprocessing หลีกเลี่ยงปัญหานี้เนื่องจากเป็นส่วนหนึ่งของโมเดล

ในตัวอย่างนี้คุณจะต้องสร้างโมเดลที่ใช้ตรรกะก่อนการประมวลผลโดยใช้ Keras functional API คุณสามารถทำได้โดยการ คลาสย่อย

API การทำงานทำงานบนเทนเซอร์ "สัญลักษณ์" เทนเซอร์ "กระตือรือร้น" ปกติมีค่า ในทางตรงกันข้ามเทนเซอร์ "สัญลักษณ์" เหล่านี้ไม่ได้ แต่จะติดตามการดำเนินการที่รันอยู่และสร้างการแสดงการคำนวณที่คุณสามารถเรียกใช้ในภายหลังได้ นี่คือตัวอย่างสั้น ๆ :

# Create a symbolic input
input = tf.keras.Input(shape=(), dtype=tf.float32)

# Do a calculation using is
result = 2*input + 1

# the result doesn't have a value
result
<tf.Tensor 'AddV2:0' shape=(None,) dtype=float32>
calc = tf.keras.Model(inputs=input, outputs=result)
print(calc(1).numpy())
print(calc(2).numpy())
3.0
5.0

ในการสร้างแบบจำลองก่อนการประมวลผลเริ่มต้นด้วยการสร้างชุดของ keras.Input สัญลักษณ์ keras.Input วัตถุที่จับคู่ชื่อและชนิดข้อมูลของคอลัมน์ CSV

inputs = {}

for name, column in titanic_features.items():
  dtype = column.dtype
  if dtype == object:
    dtype = tf.string
  else:
    dtype = tf.float32

  inputs[name] = tf.keras.Input(shape=(1,), name=name, dtype=dtype)

inputs
{'sex': <tf.Tensor 'sex:0' shape=(None, 1) dtype=string>,
 'age': <tf.Tensor 'age:0' shape=(None, 1) dtype=float32>,
 'n_siblings_spouses': <tf.Tensor 'n_siblings_spouses:0' shape=(None, 1) dtype=float32>,
 'parch': <tf.Tensor 'parch:0' shape=(None, 1) dtype=float32>,
 'fare': <tf.Tensor 'fare:0' shape=(None, 1) dtype=float32>,
 'class': <tf.Tensor 'class:0' shape=(None, 1) dtype=string>,
 'deck': <tf.Tensor 'deck:0' shape=(None, 1) dtype=string>,
 'embark_town': <tf.Tensor 'embark_town:0' shape=(None, 1) dtype=string>,
 'alone': <tf.Tensor 'alone:0' shape=(None, 1) dtype=string>}

ขั้นตอนแรกในตรรกะก่อนการประมวลผลของคุณคือการต่ออินพุตตัวเลขเข้าด้วยกันและเรียกใช้ผ่านชั้นนอร์มัลไลเซชัน:

numeric_inputs = {name:input for name,input in inputs.items()
                  if input.dtype==tf.float32}

x = layers.Concatenate()(list(numeric_inputs.values()))
norm = preprocessing.Normalization()
norm.adapt(np.array(titanic[numeric_inputs.keys()]))
all_numeric_inputs = norm(x)

all_numeric_inputs
<tf.Tensor 'normalization_1/truediv:0' shape=(None, 4) dtype=float32>

รวบรวมผลลัพธ์ก่อนการประมวลผลเชิงสัญลักษณ์ทั้งหมดเพื่อเชื่อมต่อกันในภายหลัง

preprocessed_inputs = [all_numeric_inputs]

สำหรับอินพุตสตริงให้ใช้ฟังก์ชัน preprocessing.StringLookup เพื่อแมปจากสตริงเป็นดัชนีจำนวนเต็มในคำศัพท์ จากนั้นใช้ preprocessing.CategoricalEncoding เพื่อแปลงดัชนีเป็นข้อมูล float32 ที่เหมาะสมกับโมเดล

การตั้งค่าเริ่มต้นสำหรับการเข้ารหัสแบบ CategoricalEncoding สร้างเวกเตอร์หนึ่งตัวสำหรับแต่ละอินพุต layers.Embedding ก็ใช้ได้เช่นกัน ดู คำแนะนำ เกี่ยว กับ เลเยอร์ก่อน การ ประมวลผล และ บทช่วยสอน สำหรับข้อมูลเพิ่มเติมในหัวข้อนี้

for name, input in inputs.items():
  if input.dtype == tf.float32:
    continue
  
  lookup = preprocessing.StringLookup(vocabulary=np.unique(titanic_features[name]))
  one_hot = preprocessing.CategoryEncoding(max_tokens=lookup.vocab_size())

  x = lookup(input)
  x = one_hot(x)
  preprocessed_inputs.append(x)

ด้วยการรวบรวม inputs และ inputs processed_inputs คุณสามารถเชื่อมอินพุตที่ประมวลผลล่วงหน้าทั้งหมดเข้าด้วยกันและสร้างแบบจำลองที่จัดการกับการประมวลผลก่อน:

preprocessed_inputs_cat = layers.Concatenate()(preprocessed_inputs)

titanic_preprocessing = tf.keras.Model(inputs, preprocessed_inputs_cat)

tf.keras.utils.plot_model(model = titanic_preprocessing , rankdir="LR", dpi=72, show_shapes=True)

png

model นี้มีเพียงการประมวลผลล่วงหน้าของอินพุต คุณสามารถเรียกใช้เพื่อดูว่ามันทำอะไรกับข้อมูลของคุณ โมเดล Keras จะไม่แปลง Pandas DataFrames โดยอัตโนมัติเนื่องจากไม่ชัดเจนว่าควรแปลงเป็นเทนเซอร์หนึ่งตัวหรือเป็นพจนานุกรมของเทนเซอร์ ดังนั้นแปลงเป็นพจนานุกรมของเทนเซอร์:

184bc3ea 34

ตัดตัวอย่างการฝึกอบรมแรกออกและส่งต่อไปยังโมเดลก่อนการประมวลผลนี้คุณจะเห็นคุณสมบัติตัวเลขและสตริง one-hots ทั้งหมดที่เชื่อมต่อกัน:

features_dict = {name:values[:1] for name, values in titanic_features_dict.items()}
titanic_preprocessing(features_dict)
<tf.Tensor: shape=(1, 33), dtype=float32, numpy=
array([[-0.61 ,  0.395, -0.479, -0.497,  0.   ,  0.   ,  0.   ,  1.   ,

         0.   ,  0.   ,  0.   ,  0.   ,  1.   ,  0.   ,  0.   ,  0.   ,
         0.   ,  0.   ,  0.   ,  0.   ,  0.   ,  0.   ,  1.   ,  0.   ,
         0.   ,  0.   ,  0.   ,  1.   ,  0.   ,  0.   ,  0.   ,  1.   ,
         0.   ]], dtype=float32)>

ตอนนี้สร้างแบบจำลองที่ด้านบนของสิ่งนี้:

def titanic_model(preprocessing_head, inputs):
  body = tf.keras.Sequential([
    layers.Dense(64),
    layers.Dense(1)
  ])

  preprocessed_inputs = preprocessing_head(inputs)
  result = body(preprocessed_inputs)
  model = tf.keras.Model(inputs, result)

  model.compile(loss=tf.losses.BinaryCrossentropy(from_logits=True),
                optimizer=tf.optimizers.Adam())
  return model

titanic_model = titanic_model(titanic_preprocessing, inputs)

เมื่อคุณฝึกโมเดลให้ส่งพจนานุกรมคุณสมบัติเป็น x และป้ายกำกับเป็น y

titanic_model.fit(x=titanic_features_dict, y=titanic_labels, epochs=10)
Epoch 1/10
20/20 [==============================] - 0s 3ms/step - loss: 0.6973
Epoch 2/10
20/20 [==============================] - 0s 3ms/step - loss: 0.5960
Epoch 3/10
20/20 [==============================] - 0s 3ms/step - loss: 0.5384
Epoch 4/10
20/20 [==============================] - 0s 3ms/step - loss: 0.4984
Epoch 5/10
20/20 [==============================] - 0s 3ms/step - loss: 0.4712
Epoch 6/10
20/20 [==============================] - 0s 3ms/step - loss: 0.4527
Epoch 7/10
20/20 [==============================] - 0s 3ms/step - loss: 0.4423
Epoch 8/10
20/20 [==============================] - 0s 3ms/step - loss: 0.4342
Epoch 9/10
20/20 [==============================] - 0s 3ms/step - loss: 0.4288
Epoch 10/10
20/20 [==============================] - 0s 3ms/step - loss: 0.4262

<tensorflow.python.keras.callbacks.History at 0x7f58395e0cc0>

เนื่องจากการประมวลผลล่วงหน้าเป็นส่วนหนึ่งของโมเดลคุณสามารถบันทึกโมเดลและโหลดซ้ำที่อื่นและได้ผลลัพธ์ที่เหมือนกัน:

titanic_model.save('test')
reloaded = tf.keras.models.load_model('test')
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/training/tracking/tracking.py:111: Model.state_updates (from tensorflow.python.keras.engine.training) is deprecated and will be removed in a future version.
Instructions for updating:
This property should not be used in TensorFlow 2.0, as updates are applied automatically.
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/training/tracking/tracking.py:111: Layer.updates (from tensorflow.python.keras.engine.base_layer) is deprecated and will be removed in a future version.
Instructions for updating:
This property should not be used in TensorFlow 2.0, as updates are applied automatically.
INFO:tensorflow:Assets written to: test/assets
WARNING:tensorflow:5 out of the last 5 calls to <function recreate_function.<locals>.restored_function_body at 0x7f57b0154d90> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings could be due to (1) creating @tf.function repeatedly in a loop, (2) passing tensors with different shapes, (3) passing Python objects instead of tensors. For (1), please define your @tf.function outside of the loop. For (2), @tf.function has experimental_relax_shapes=True option that relaxes argument shapes that can avoid unnecessary retracing. For (3), please refer to https://www.tensorflow.org/tutorials/customization/performance#python_or_tensor_args and https://www.tensorflow.org/api_docs/python/tf/function for  more details.

features_dict = {name:values[:1] for name, values in titanic_features_dict.items()}

before = titanic_model(features_dict)
after = reloaded(features_dict)
assert (before-after)<1e-3
print(before)
print(after)
tf.Tensor([[-1.808]], shape=(1, 1), dtype=float32)
tf.Tensor([[-1.808]], shape=(1, 1), dtype=float32)

ใช้ tf.data

ในส่วนก่อนหน้านี้คุณใช้การสับและจัดกลุ่มข้อมูลในตัวของโมเดลในขณะที่ฝึกโมเดล

หากคุณต้องการควบคุมท่อส่งข้อมูลอินพุตมากขึ้นหรือต้องการใช้ข้อมูลที่ไม่พอดีกับหน่วยความจำให้ใช้ tf.data

สำหรับตัวอย่างเพิ่มเติมโปรดดู คู่มือ tf.data

เปิดในข้อมูลหน่วยความจำ

ดังตัวอย่างแรกของการใช้ tf.data กับข้อมูล CSV ให้พิจารณาโค้ดต่อไปนี้เพื่อแบ่งพจนานุกรมคุณลักษณะจากส่วนก่อนหน้าด้วยตนเอง สำหรับแต่ละดัชนีจะใช้ดัชนีนั้นสำหรับแต่ละคุณลักษณะ:

import itertools

def slices(features):
  for i in itertools.count():
    # For each feature take index `i`
    example = {name:values[i] for name, values in features.items()}
    yield example

เรียกใช้สิ่งนี้และพิมพ์ตัวอย่างแรก:

for example in slices(titanic_features_dict):
  for name, value in example.items():
    print(f"{name:19s}: {value}")
  break
sex                : male
age                : 22.0
n_siblings_spouses : 1
parch              : 0
fare               : 7.25
class              : Third
deck               : unknown
embark_town        : Southampton
alone              : n

tf.data.Dataset ขั้นพื้นฐานที่สุด tf.data.Dataset ข้อมูลในตัวโหลดข้อมูลหน่วยความจำคือตัวสร้าง Dataset.from_tensor_slices ผลตอบแทนนี้ tf.data.Dataset ว่าการดำเนินการรุ่นทั่วไปของดังกล่าวข้างต้น slices ทำงานใน TensorFlow

features_ds = tf.data.Dataset.from_tensor_slices(titanic_features_dict)

คุณสามารถวนซ้ำชุดข้อมูล tf.data.Dataset เหมือนกับ python อื่น ๆ ที่ทำซ้ำได้:

for example in features_ds:
  for name, value in example.items():
    print(f"{name:19s}: {value}")
  break
sex                : b'male'
age                : 22.0
n_siblings_spouses : 1
parch              : 0
fare               : 7.25
class              : b'Third'
deck               : b'unknown'
embark_town        : b'Southampton'
alone              : b'n'

ฟังก์ชัน from_tensor_slices สามารถจัดการกับโครงสร้างใด ๆ ของพจนานุกรมหรือสิ่งที่ซ้อนกัน รหัสต่อไปนี้สร้างชุดข้อมูลของคู่ (features_dict, labels) :

titanic_ds = tf.data.Dataset.from_tensor_slices((titanic_features_dict, titanic_labels))

ในการฝึกโมเดลโดยใช้ Dataset นี้อย่างน้อยคุณจะต้อง shuffle และจัด batch ข้อมูล

titanic_batches = titanic_ds.shuffle(len(titanic_labels)).batch(32)

แทนที่จะส่ง features และ labels ไปยัง Model.fit คุณส่งชุดข้อมูล:

titanic_model.fit(titanic_batches, epochs=5)
Epoch 1/5
20/20 [==============================] - 0s 4ms/step - loss: 0.4231
Epoch 2/5
20/20 [==============================] - 0s 4ms/step - loss: 0.4220
Epoch 3/5
20/20 [==============================] - 0s 4ms/step - loss: 0.4217
Epoch 4/5
20/20 [==============================] - 0s 4ms/step - loss: 0.4212
Epoch 5/5
20/20 [==============================] - 0s 4ms/step - loss: 0.4203

<tensorflow.python.keras.callbacks.History at 0x7f57d9fe3da0>

จากไฟล์เดียว

จนถึงตอนนี้บทช่วยสอนนี้ใช้งานได้กับข้อมูลในหน่วยความจำ tf.data เป็นชุดเครื่องมือที่ปรับขนาดได้สูงสำหรับการสร้างไปป์ไลน์ข้อมูลและมีฟังก์ชันบางอย่างสำหรับจัดการการโหลดไฟล์ CSV

titanic_file_path = tf.keras.utils.get_file("train.csv", "https://storage.googleapis.com/tf-datasets/titanic/train.csv")
Downloading data from https://storage.googleapis.com/tf-datasets/titanic/train.csv
32768/30874 [===============================] - 0s 0us/step

ตอนนี้อ่านข้อมูล CSV จากไฟล์และสร้าง tf.data.Dataset

(สำหรับเอกสารฉบับเต็มโปรดดูที่ tf.data.experimental.make_csv_dataset )

titanic_csv_ds = tf.data.experimental.make_csv_dataset(
    titanic_file_path,
    batch_size=5, # Artificially small to make examples easier to show.
    label_name='survived',
    num_epochs=1,
    ignore_errors=True,)

ฟังก์ชันนี้มีคุณสมบัติที่สะดวกมากมายเพื่อให้ใช้งานข้อมูลได้ง่าย ซึ่งรวมถึง:

  • ใช้ส่วนหัวของคอลัมน์เป็นคีย์พจนานุกรม
  • กำหนดประเภทของแต่ละคอลัมน์โดยอัตโนมัติ
for batch, label in titanic_csv_ds.take(1):
  for key, value in batch.items():
    print(f"{key:20s}: {value}")
  print()
  print(f"{'label':20s}: {label}")
sex                 : [b'male' b'female' b'female' b'female' b'female']
age                 : [28. 22. 28. 35. 48.]
n_siblings_spouses  : [0 1 0 0 1]
parch               : [0 1 0 0 2]
fare                : [  7.312  29.      7.879 512.329  65.   ]
class               : [b'Third' b'Second' b'Third' b'First' b'Second']
deck                : [b'unknown' b'unknown' b'unknown' b'unknown' b'unknown']
embark_town         : [b'Southampton' b'Southampton' b'Queenstown' b'Cherbourg' b'Southampton']
alone               : [b'y' b'n' b'y' b'y' b'n']

label               : [0 1 1 1 1]

นอกจากนี้ยังสามารถขยายขนาดข้อมูลได้ทันที นี่คือไฟล์ CSV gzipped ที่มี ชุดข้อมูลการจราจรระหว่างรัฐในเมืองใหญ่

การจราจรติดขัด.

ภาพ จาก Wikimedia

traffic_volume_csv_gz = tf.keras.utils.get_file(
    'Metro_Interstate_Traffic_Volume.csv.gz', 
    "https://archive.ics.uci.edu/ml/machine-learning-databases/00492/Metro_Interstate_Traffic_Volume.csv.gz",
    cache_dir='.', cache_subdir='traffic')
Downloading data from https://archive.ics.uci.edu/ml/machine-learning-databases/00492/Metro_Interstate_Traffic_Volume.csv.gz
409600/405373 [==============================] - 1s 2us/step

ตั้งค่าอาร์กิวเมนต์ compression_type เพื่ออ่านโดยตรงจากไฟล์บีบอัด:

traffic_volume_csv_gz_ds = tf.data.experimental.make_csv_dataset(
    traffic_volume_csv_gz,
    batch_size=256,
    label_name='traffic_volume',
    num_epochs=1,
    compression_type="GZIP")

for batch, label in traffic_volume_csv_gz_ds.take(1):
  for key, value in batch.items():
    print(f"{key:20s}: {value[:5]}")
  print()
  print(f"{'label':20s}: {label[:5]}")
holiday             : [b'None' b'None' b'None' b'None' b'None']
temp                : [271.6  286.1  268.77 254.46 276.37]
rain_1h             : [0. 0. 0. 0. 0.]
snow_1h             : [0. 0. 0. 0. 0.]
clouds_all          : [90 92 64 56 90]
weather_main        : [b'Snow' b'Clouds' b'Mist' b'Clouds' b'Clouds']
weather_description : [b'heavy snow' b'overcast clouds' b'mist' b'broken clouds'
 b'overcast clouds']
date_time           : [b'2012-11-23 20:00:00' b'2013-06-09 02:00:00' b'2013-11-08 06:00:00'
 b'2013-02-02 14:00:00' b'2012-12-06 20:00:00']

label               : [2543 1432 5638 4684 2983]

เก็บเอาไว้

มีค่าใช้จ่ายบางส่วนในการแยกวิเคราะห์ข้อมูล csv สำหรับรุ่นเล็กสิ่งนี้อาจเป็นคอขวดในการฝึกอบรม

ขึ้นอยู่กับกรณีการใช้งานของคุณคุณควรใช้ Dataset.cache หรือ data.experimental.snapshot เพื่อให้ข้อมูล csv ถูกแยกวิเคราะห์ในยุคแรกเท่านั้น

ความแตกต่างหลักระหว่างวิธี cache และส snapshot คือไฟล์ cache สามารถใช้ได้เฉพาะในกระบวนการ TensorFlow ที่สร้างขึ้น แต่กระบวนการอื่น ๆ สามารถอ่านไฟล์ส snapshot ได้

ตัวอย่างเช่นการวนซ้ำบน traffic_volume_csv_gz_ds 20 ครั้งใช้เวลา ~ 15 วินาทีโดยไม่ต้องแคชหรือ ~ 2 วินาทีด้วยการแคช

%%time
for i, (batch, label) in enumerate(traffic_volume_csv_gz_ds.repeat(20)):
  if i % 40 == 0:
    print('.', end='')
print()
...............................................................................................
CPU times: user 16.3 s, sys: 3.78 s, total: 20.1 s
Wall time: 12.8 s

%%time
caching = traffic_volume_csv_gz_ds.cache().shuffle(1000)

for i, (batch, label) in enumerate(caching.shuffle(1000).repeat(20)):
  if i % 40 == 0:
    print('.', end='')
print()
...............................................................................................
CPU times: user 1.57 s, sys: 135 ms, total: 1.71 s
Wall time: 1.4 s

%%time
snapshot = tf.data.experimental.snapshot('titanic.tfsnap')
snapshotting = traffic_volume_csv_gz_ds.apply(snapshot).shuffle(1000)

for i, (batch, label) in enumerate(snapshotting.shuffle(1000).repeat(20)):
  if i % 40 == 0:
    print('.', end='')
print()
...............................................................................................
CPU times: user 2.28 s, sys: 418 ms, total: 2.7 s
Wall time: 1.76 s

หากการโหลดข้อมูลของคุณช้าลงเนื่องจากการโหลดไฟล์ csv และ cache และส snapshot ไม่เพียงพอสำหรับกรณีการใช้งานของคุณให้พิจารณาเข้ารหัสข้อมูลของคุณใหม่ให้อยู่ในรูปแบบที่คล่องตัวยิ่งขึ้น

หลายไฟล์

จนถึงตอนนี้ตัวอย่างทั้งหมดในส่วนนี้สามารถทำได้อย่างง่ายดายโดยไม่ต้องใช้ tf.data สถานที่หนึ่งที่ tf.data สามารถทำให้สิ่งต่างๆง่ายขึ้นคือเมื่อจัดการกับคอลเล็กชันของไฟล์

ตัวอย่างเช่นชุดข้อมูล รูปภาพฟอนต์อักขระ จะถูกแจกจ่ายเป็นคอลเล็กชันของไฟล์ csv หนึ่งไฟล์ต่อฟอนต์

แบบอักษร

ภาพโดย Willi Heidelbach จาก Pixabay

ดาวน์โหลดชุดข้อมูลและดูไฟล์ภายใน:

fonts_zip = tf.keras.utils.get_file(
    'fonts.zip',  "https://archive.ics.uci.edu/ml/machine-learning-databases/00417/fonts.zip",
    cache_dir='.', cache_subdir='fonts',
    extract=True)
Downloading data from https://archive.ics.uci.edu/ml/machine-learning-databases/00417/fonts.zip
160317440/160313983 [==============================] - 8s 0us/step

import pathlib
font_csvs =  sorted(str(p) for p in pathlib.Path('fonts').glob("*.csv"))

font_csvs[:10]
['fonts/AGENCY.csv',
 'fonts/ARIAL.csv',
 'fonts/BAITI.csv',
 'fonts/BANKGOTHIC.csv',
 'fonts/BASKERVILLE.csv',
 'fonts/BAUHAUS.csv',
 'fonts/BELL.csv',
 'fonts/BERLIN.csv',
 'fonts/BERNARD.csv',
 'fonts/BITSTREAMVERA.csv']
len(font_csvs)
153

เมื่อจัดการกับพวงของไฟล์ที่คุณสามารถผ่าน glob สไตล์ file_pattern ไป experimental.make_csv_dataset ฟังก์ชั่น ลำดับของไฟล์จะถูกสับการวนซ้ำแต่ละครั้ง

ใช้อาร์กิวเมนต์ num_parallel_reads เพื่อกำหนดจำนวนไฟล์ที่อ่านแบบขนานและแทรกระหว่างกัน

fonts_ds = tf.data.experimental.make_csv_dataset(
    file_pattern = "fonts/*.csv",
    batch_size=10, num_epochs=1,
    num_parallel_reads=20,
    shuffle_buffer_size=10000)

ไฟล์ csv เหล่านี้จะมีการแบ่งภาพออกเป็นแถวเดียว ชื่อคอลัมน์อยู่ในรูปแบบ r{row}c{column} นี่คือชุดแรก:

for features in fonts_ds.take(1):
  for i, (name, value) in enumerate(features.items()):
    if i>15:
      break
    print(f"{name:20s}: {value}")
print('...')
print(f"[total: {len(features)} features]")
font                : [b'MONOTYPE' b'MV_BOLI' b'MAIANDRA' b'BODONI' b'VLADIMIR' b'MAIANDRA'
 b'MAIANDRA' b'JUICE' b'CAMBRIA' b'RICHARD']
fontVariant         : [b'MONOTYPE CORSIVA' b'MV BOLI' b'MAIANDRA GD'
 b'BODONI MT POSTER COMPRESSED' b'VLADIMIR SCRIPT' b'MAIANDRA GD'
 b'MAIANDRA GD' b'JUICE ITC' b'CAMBRIA' b'POOR RICHARD']
m_label             : [ 1059  1946   710   200   120   246  8211   224 10694   733]
strength            : [0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4]
italic              : [0 0 0 1 0 0 1 0 0 0]
orientation         : [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
m_top               : [38 61 38 20 61 40 67 35 46 41]
m_left              : [25 16 27 21 12 23 25 22 24 26]
originalH           : [52 33 10 62 19 45  4 46 38 10]
originalW           : [57 39 20 34 37 35 35 21 39 22]
h                   : [20 20 20 20 20 20 20 20 20 20]
w                   : [20 20 20 20 20 20 20 20 20 20]
r0c0                : [  1   1   1   1   1   1   1   1 255   1]
r0c1                : [  1   1   1   1   1   1 255   1 255   1]
r0c2                : [  1   1   1   1   1   1 255  16 255   1]
r0c3                : [  1   1   1   1   1   1 255 111 255   1]
...
[total: 412 features]

ไม่บังคับ: ช่องบรรจุ

คุณอาจไม่ต้องการทำงานกับแต่ละพิกเซลในคอลัมน์แยกกันเช่นนี้ ก่อนที่จะพยายามใช้ชุดข้อมูลนี้โปรดแน่ใจว่าได้บรรจุพิกเซลลงใน Image-tensor

นี่คือโค้ดที่แยกวิเคราะห์ชื่อคอลัมน์เพื่อสร้างรูปภาพสำหรับแต่ละตัวอย่าง:

import re

def make_images(features):
  image = [None]*400
  new_feats = {}

  for name, value in features.items():
    match = re.match('r(\d+)c(\d+)', name)
    if match:
      image[int(match.group(1))*20+int(match.group(2))] = value
    else:
      new_feats[name] = value

  image = tf.stack(image, axis=0)
  image = tf.reshape(image, [20, 20, -1])
  new_feats['image'] = image

  return new_feats

ใช้ฟังก์ชันนั้นกับแต่ละชุดในชุดข้อมูล:

fonts_image_ds = fonts_ds.map(make_images)

for features in fonts_image_ds.take(1):
  break

พล็อตภาพที่ได้:

from matplotlib import pyplot as plt

plt.figure(figsize=(6,6), dpi=120)

for n in range(9):
  plt.subplot(3,3,n+1)
  plt.imshow(features['image'][..., n])
  plt.title(chr(features['m_label'][n]))
  plt.axis('off')

png

ฟังก์ชั่นระดับล่าง

จนถึงตอนนี้บทช่วยสอนนี้มุ่งเน้นไปที่ยูทิลิตี้ระดับสูงสุดสำหรับการอ่านข้อมูล csv มี API อีกสองรายการที่อาจเป็นประโยชน์สำหรับผู้ใช้ขั้นสูงหากกรณีการใช้งานของคุณไม่ตรงกับรูปแบบพื้นฐาน

  • tf.io.decode_csv - ฟังก์ชั่นสำหรับการแยกวิเคราะห์บรรทัดของข้อความในรายการเทนเซอร์คอลัมน์ CSV
  • tf.data.experimental.CsvDataset - ตัวสร้างชุดข้อมูล csv ระดับล่าง

ส่วนนี้จะสร้างฟังก์ชันใหม่ที่จัดทำโดย make_csv_dataset เพื่อสาธิตวิธีการใช้งานฟังก์ชันระดับล่างนี้

tf.io.decode_csv

ฟังก์ชันนี้จะถอดรหัสสตริงหรือรายการสตริงลงในรายการคอลัมน์

ต่างจาก make_csv_dataset ฟังก์ชันนี้ไม่พยายามเดาประเภทข้อมูลคอลัมน์ คุณระบุประเภทคอลัมน์โดยระบุรายการ record_defaults ที่มีค่าของประเภทที่ถูกต้องสำหรับแต่ละคอลัมน์

หากต้องการอ่านข้อมูล Titanic เป็นสตริง โดยใช้ decode_csv คุณจะพูดว่า:

text = pathlib.Path(titanic_file_path).read_text()
lines = text.split('\n')[1:-1]

all_strings = [str()]*10
all_strings
['', '', '', '', '', '', '', '', '', '']
features = tf.io.decode_csv(lines, record_defaults=all_strings) 

for f in features:
  print(f"type: {f.dtype.name}, shape: {f.shape}")
type: string, shape: (627,)
type: string, shape: (627,)
type: string, shape: (627,)
type: string, shape: (627,)
type: string, shape: (627,)
type: string, shape: (627,)
type: string, shape: (627,)
type: string, shape: (627,)
type: string, shape: (627,)
type: string, shape: (627,)

หากต้องการแยกวิเคราะห์ด้วยประเภทจริงให้สร้างรายการ record_defaults ของประเภทที่เกี่ยวข้อง:

print(lines[0])
0,male,22.0,1,0,7.25,Third,unknown,Southampton,n

titanic_types = [int(), str(), float(), int(), int(), float(), str(), str(), str(), str()]
titanic_types
[0, '', 0.0, 0, 0, 0.0, '', '', '', '']
features = tf.io.decode_csv(lines, record_defaults=titanic_types) 

for f in features:
  print(f"type: {f.dtype.name}, shape: {f.shape}")
type: int32, shape: (627,)
type: string, shape: (627,)
type: float32, shape: (627,)
type: int32, shape: (627,)
type: int32, shape: (627,)
type: float32, shape: (627,)
type: string, shape: (627,)
type: string, shape: (627,)
type: string, shape: (627,)
type: string, shape: (627,)

tf.data.experimental.CsvDataset

คลาส tf.data.experimental.CsvDataset มีอินเทอร์เฟซ CSV Dataset น้อยที่สุดโดยไม่มีคุณสมบัติอำนวยความสะดวกของฟังก์ชัน make_csv_dataset : การแยกวิเคราะห์ส่วนหัวของคอลัมน์, การอนุมานประเภทคอลัมน์, การสับอัตโนมัติ, การแทรกไฟล์

ตัวสร้างนี้ใช้ record_defaults แบบเดียวกับ io.parse_csv :

simple_titanic = tf.data.experimental.CsvDataset(titanic_file_path, record_defaults=titanic_types, header=True)

for example in simple_titanic.take(1):
  print([e.numpy() for e in example])
[0, b'male', 22.0, 1, 0, 7.25, b'Third', b'unknown', b'Southampton', b'n']

รหัสข้างต้นโดยทั่วไปเทียบเท่ากับ:

def decode_titanic_line(line):
  return tf.io.decode_csv(line, titanic_types)

manual_titanic = (
    # Load the lines of text
    tf.data.TextLineDataset(titanic_file_path)
    # Skip the header row.
    .skip(1)
    # Decode the line.
    .map(decode_titanic_line)
)

for example in manual_titanic.take(1):
  print([e.numpy() for e in example])
[0, b'male', 22.0, 1, 0, 7.25, b'Third', b'unknown', b'Southampton', b'n']

หลายไฟล์

ในการแยกวิเคราะห์ชุดข้อมูลแบบอักษรโดยใช้ชุดข้อมูล experimental.CsvDataset ก่อนอื่นคุณต้องกำหนดประเภทคอลัมน์สำหรับ record_defaults เริ่มต้นด้วยการตรวจสอบแถวแรกของไฟล์เดียว:

font_line = pathlib.Path(font_csvs[0]).read_text().splitlines()[1]
print(font_line)
AGENCY,AGENCY FB,64258,0.400000,0,0.000000,35,21,51,22,20,20,1,1,1,21,101,210,255,255,255,255,255,255,255,255,255,255,255,255,255,255,1,1,1,93,255,255,255,176,146,146,146,146,146,146,146,146,216,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,141,141,141,182,255,255,255,172,141,141,141,115,1,1,1,1,163,255,255,255,255,255,255,255,255,255,255,255,255,255,255,209,1,1,1,1,163,255,255,255,6,6,6,96,255,255,255,74,6,6,6,5,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255

เฉพาะสองช่องแรกเท่านั้นที่เป็นสตริงส่วนที่เหลือเป็น ints หรือ float และคุณสามารถรับจำนวนคุณสมบัติทั้งหมดได้โดยการนับเครื่องหมายจุลภาค:

num_font_features = font_line.count(',')+1
font_column_types = [str(), str()] + [float()]*(num_font_features-2)

ตัวสร้าง CsvDatasaet สามารถรับรายการไฟล์อินพุตได้ แต่จะอ่านตามลำดับ ไฟล์แรกในรายการ CSV คือ AGENCY.csv :

font_csvs[0]
'fonts/AGENCY.csv'

ดังนั้นเมื่อคุณส่งรายการไฟล์ไปยัง CsvDataaset ระเบียนจาก AGENCY.csv จะถูกอ่านก่อน:

simple_font_ds = tf.data.experimental.CsvDataset(
    font_csvs, 
    record_defaults=font_column_types, 
    header=True)
for row in simple_font_ds.take(10):
  print(row[0].numpy())
b'AGENCY'
b'AGENCY'
b'AGENCY'
b'AGENCY'
b'AGENCY'
b'AGENCY'
b'AGENCY'
b'AGENCY'
b'AGENCY'
b'AGENCY'

หากต้องการแทรกไฟล์หลายไฟล์ให้ใช้ Dataset.interleave

นี่คือชุดข้อมูลเริ่มต้นที่มีชื่อไฟล์ csv:

font_files = tf.data.Dataset.list_files("fonts/*.csv")

สิ่งนี้จะสลับชื่อไฟล์แต่ละยุค:

print('Epoch 1:')
for f in list(font_files)[:5]:
  print("    ", f.numpy())
print('    ...')
print()

print('Epoch 2:')
for f in list(font_files)[:5]:
  print("    ", f.numpy())
print('    ...')
Epoch 1:
     b'fonts/REFERENCE.csv'
     b'fonts/ONYX.csv'
     b'fonts/BODONI.csv'
     b'fonts/CALIFORNIAN.csv'
     b'fonts/SIMPLEX.csv'
    ...

Epoch 2:
     b'fonts/BANKGOTHIC.csv'
     b'fonts/BELL.csv'
     b'fonts/HARRINGTON.csv'
     b'fonts/IMPRINT.csv'
     b'fonts/EUROROMAN.csv'
    ...

เมธอด interleave ใช้ map_func ที่สร้าง Dataset สำหรับแต่ละองค์ประกอบของ Dataset ต์

ที่นี่คุณต้องการสร้าง CsvDataset จากแต่ละองค์ประกอบของชุดข้อมูลของไฟล์:

def make_font_csv_ds(path):
  return tf.data.experimental.CsvDataset(
    path, 
    record_defaults=font_column_types, 
    header=True)

Dataset ส่งคืนโดย interleave จะส่งคืนองค์ประกอบโดยการวนรอบ Dataset ย่อยจำนวนหนึ่ง หมายเหตุด้านล่างวิธีที่ชุดข้อมูลหมุนเวียนในช่วง cycle_length)=3 ไฟล์แบบอักษร 3 ไฟล์:

font_rows = font_files.interleave(make_font_csv_ds,
                                  cycle_length=3)
fonts_dict = {'font_name':[], 'character':[]}

for row in font_rows.take(10):
  fonts_dict['font_name'].append(row[0].numpy().decode())
  fonts_dict['character'].append(chr(row[2].numpy()))

pd.DataFrame(fonts_dict)

ประสิทธิภาพ

ก่อนหน้านี้มีการสังเกตว่า io.decode_csv มีประสิทธิภาพมากกว่าเมื่อรันบนชุดสตริง

เป็นไปได้ที่จะใช้ประโยชน์จากข้อเท็จจริงนี้เมื่อใช้ชุดงานขนาดใหญ่เพื่อปรับปรุงประสิทธิภาพการโหลด CSV (แต่ลอง แคช ก่อน)

ด้วยตัวโหลดในตัว 20 ชุดตัวอย่าง 2048 จะใช้เวลาประมาณ 17 วินาที

BATCH_SIZE=2048
fonts_ds = tf.data.experimental.make_csv_dataset(
    file_pattern = "fonts/*.csv",
    batch_size=BATCH_SIZE, num_epochs=1,
    num_parallel_reads=100)
%%time
for i,batch in enumerate(fonts_ds.take(20)):
  print('.',end='')

print()
....................
CPU times: user 27.4 s, sys: 1.67 s, total: 29.1 s
Wall time: 10.7 s

การส่งผ่าน ชุดข้อความ ไปยัง decode_csv จะทำงานได้เร็วขึ้นในเวลาประมาณ 5 decode_csv :

fonts_files = tf.data.Dataset.list_files("fonts/*.csv")
fonts_lines = fonts_files.interleave(
    lambda fname:tf.data.TextLineDataset(fname).skip(1), 
    cycle_length=100).batch(BATCH_SIZE)

fonts_fast = fonts_lines.map(lambda x: tf.io.decode_csv(x, record_defaults=font_column_types))
%%time
for i,batch in enumerate(fonts_fast.take(20)):
  print('.',end='')

print()
....................
CPU times: user 5.27 s, sys: 0 ns, total: 5.27 s
Wall time: 4.75 s

สำหรับอีกตัวอย่างหนึ่งของการเพิ่มประสิทธิภาพ csv โดยใช้แบทช์ขนาดใหญ่โปรดดู บทแนะนำการสวมใส่และชุด ชั้นใน

วิธีการแบบนี้อาจใช้ได้ผล แต่ให้พิจารณาตัวเลือกอื่น ๆ เช่น cache และส snapshot หรือเข้ารหัสข้อมูลของคุณใหม่ให้อยู่ในรูปแบบที่คล่องตัวยิ่งขึ้น