ASAC/DL
[ASAC]CNN 1일차 코드
standingR
2024. 5. 28. 11:18
In [ ]:
In [ ]:
from IPython.core.display import display, HTML
display(HTML("<style>.container {width:90% !important;}</style>"))
In [ ]:
# GPU는 무조건 T4라도 연결하고 사용하셔야 합니다!!!!!!!
In [ ]:
import tensorflow as tf
In [ ]:
# 3차원 이미지를 대상으로 CNN구조를 만들 때!!!!
# --> 집중의 대상은 2차원 사이즈 중심!!!
# ( 뒤에 있는 채널에 대한 것들은 생략!!!!!--> 니가 알아서 연결)
# ==> 필터의 수와 연동이 되니까...코드가 알아서 해주라!!!
In [ ]:
# 1) Conv2D 레이어에 대한 세팅!!(채널은 신경 안 쓰겠다!!!)
# - kernel_size : 필터에 대한 2차원 사이즈(어느 사이즈로 스캔할지)
# 3*3, 5*5, 7*7 etc 특징들을 추출..,
# ==> Output인 Feature Map의 사이즈에 연동!!!
# - filters : 몇 개의 필터를 사용할지.....
# <==> Feature Map을 몇 장 만들어 낼지!!!!!!!
# output인 Feature Map의 Channel의 수!!!
# - stride : 가로 step, 세로 step
# 얼마나 자세하게 스캐닝을 할지/말지..
# ==> Feature Map의 Size에 연관!!!!
# - padding : 테두리에 대한 처리 ( 사이즈 보전을 할지 말지... )
# valid : 그냥 테두리 처리 없이 스캐닝을 하자
# same : 니가 내가 지정한 kernel_size에 맞춰서
# 입력과 동을한 Feature Map이 나오도록
# 테두리 처리를 해주라!!!
# +++ ActivationFunction : 초기 버전들은 AF을 사용X
#
In [ ]:
conv1 = tf.keras.layers.Conv2D(
# 우리가 신경쓸 부분은 오로지 2d만 신경씀!!
# 나머지 : channel은 코드가 알아서 해주라!!!
kernel_size = (5,5),
filters = 4,
# 가로 세로 한칸식 점프
strides = (2,2),
padding = "same"
## ++ 최근에는 AF Funtion
#activation = "relu"
)
In [ ]:
In [ ]:
# Pooling Layer
# --> Feature Map의 결과들을 대표화를 하는 과정!!!!!
# ( 일반적으로 사이즈들을 줄이는 것을 선호!!!!!)
# - pool_size : 처리가 되는 단위 : 대표화의 단위( 2,2)
# - strides : 옵션도 같이 활용....
pool1 = tf.keras.layers.MaxPool2D(
pool_size = (2,2),
strides= (2,2)
)
In [ ]:
# + 연결이 만아서...
# ---> 중간에 적당하게 연결을 짜르는 Drop Out
# 참고) 최종적인 FeatureMap을 분류 DNN 구조와 연결을 할 떄
# FullConnerction을 주로 활용을 함... (야기서는 Drop OUT 사용하지 않음! )
In [ ]:
In [ ]:
#### Fashion - MNIST Set을 바탕으로 DNN 구조 열심히 했음
# 설계 방식을 CNN 구조로 도압을 하자
In [ ]:
fashion_mnist = tf.keras.datasets.fashion_mnist
(train_X, train_y), (test_X, test_y) = fashion_mnist.load_data()
print(train_X.shape)
print(train_y.shape)
print(test_X.shape)
print(test_y.shape)
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-labels-idx1-ubyte.gz 29515/29515 [==============================] - 0s 0us/step Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-images-idx3-ubyte.gz 26421880/26421880 [==============================] - 0s 0us/step Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-labels-idx1-ubyte.gz 5148/5148 [==============================] - 0s 0us/step Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-images-idx3-ubyte.gz 4422102/4422102 [==============================] - 0s 0us/step (60000, 28, 28) (60000,) (10000, 28, 28) (10000,)
In [ ]:
# 전처리 : 이미지 전처리 ====> 1/ 255,0 으로 정규화
train_X = train_X/255.0
test_X = test_X/255.0
In [ ]:
# ===> 간단하게 하기 위해서 흑백 사진을 사용을 하였음.
# 보통은 컬러 이미지를 가지고 수행을 함!!
# + 컬러 자체로 할지 // 흑백으로 채널을 줄여서 할지 ... 결정!!!
# (컬러의 특징이 중요하지 않을 때....)
# ==> 전통적인 이미지 처리 : openCV (c/c++ --> 모든 언어)
# colab openCV는 일반적인 opencv와 채널의 순거가 다름...
# 푸르딩딩(스머프로) 나옴;; RGB --> BGR
#
In [ ]:
In [ ]:
# 1. 입력에 대한 데이터 처리!!
# ==> 1장에 대해서 처리 !!!!
# n장에 대해서 일괄 처리는 코드가 TF가 알아서 함..
# 1장에 댛서 잘 코드화
In [ ]:
# 3차원
train_X.shape
Out[ ]:
(60000, 28, 28)
In [ ]:
train_X[0].shape # 2d
Out[ ]:
(28, 28)
In [ ]:
# 3차원 데이터로 변경
### 전처리 : 내가 편하게 채널은 신경안쓰고 사용하기 위해서
# Conv2d를 사용하려고 함!!
# 1장에 대한 데이터는 3d로 들어가야 함!!!
# (채널은 내가 신경 안 쓰고 하기 위해서)
# ===> 데이터의 모양을 변경을 해야함!!! (차원/모양)
# 지금 데이터 1장 : 28 x 28
# 지금 데이터셋 : 6000, 28 x 28
# 원하는 입력 데이터 셋 : 60000, 28, 28, 1 [4D]
"""
==> 이런식으로 입력 데이터셋을 변경을 해야
그 뒤로 내가 신경 쓸 일이 없어요!! 미리 세팅을 함!
(주의!!!) 채널에 대한 정보는 주로 뒤로 세팅!!!
(28,28,1, 600000), (600000, 1, 28, 28)
==> (데이터수, 가로, 세로, 채널)
(600000, 28, 28, 1)
위와 같이 셋이 구성이 되어야, CONV2d 사용을 할수 있다.
"""
Out[ ]:
'\n==> 이런식으로 입력 데이터셋을 변경을 해야\n 그 뒤로 내가 신경 쓸 일이 없어요!! 미리 세팅을 함!\n (주의!!!) 채널에 대한 정보는 주로 뒤로 세팅!!!\n (28,28,1, 600000), (600000, 1, 28, 28)\n ==> (데이터수, 가로, 세로, 채널)\n (600000, 28, 28, 1)\n 위와 같이 셋이 구성이 되어야, CONV2d 사용을 할수 있다. \n'
In [ ]:
print(train_X.shape)
print(train_X[0].shape)
# -1 의 의미 나머지는 알앗허 꼽아주세요
train_X = train_X.reshape(-1, 28, 28, 1) # 3d ---> 4d
test_X = test_X.reshape(-1, 28, 28, 1)
print("4D :",train_X.shape)
print("4D :",train_X[0].shape)
(60000, 28, 28) (28, 28) 4D : (60000, 28, 28, 1) 4D : (28, 28, 1)
In [ ]:
## 가장 간단한 CNN 구조 모델
model = tf.keras.Sequential([
#입력 : 1장 데이터를 기준 --> 28,28,1
#1번 CONV 레이어 + 입력을 받는 기능까지
tf.keras.layers.Conv2D(input_shape=(28,28,1), kernel_size=(3,3),filters=16),
# 2번
tf.keras.layers.Conv2D(kernel_size=(3,3),filters=32),
# 3번
tf.keras.layers.Conv2D(kernel_size=(3,3),filters=64),
# 이미지가 가진 특증일 잘뽑아냈다고 가정
# step2) 분류를 위한 DNN 구조
# ***특징을 쭉~~~1Dckdnjs
tf.keras.layers.Flatten(),
tf.keras.layers.Dense( units = 126, activation="relu"),
# 출력용
tf.keras.layers.Dense( units=10, activation="relu")
])
In [ ]:
model.summary()
Model: "sequential" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= conv2d_1 (Conv2D) (None, 26, 26, 16) 160 conv2d_2 (Conv2D) (None, 24, 24, 32) 4640 conv2d_3 (Conv2D) (None, 22, 22, 64) 18496 flatten (Flatten) (None, 30976) 0 dense (Dense) (None, 126) 3903102 dense_1 (Dense) (None, 10) 1270 ================================================================= Total params: 3927668 (14.98 MB) Trainable params: 3927668 (14.98 MB) Non-trainable params: 0 (0.00 Byte) _________________________________________________________________
▶ 편향(bias) 뉴런의 활성화 조건을 결정하는 매개변수입니다. 활성화 함수에서 설정된 임계값을 얼마나 쉽게 넘게 할 것인지 결정해줍니다. 편향이 높다면 그만큼 활성화 함수의 임계값을 넘기 어렵기 때문에 까다로운 모델
In [ ]:
"""
1번 Conv의 파라미터의 수
"""
(3 * 3 + 1) * 19
Out[ ]:
190
In [ ]:
(3 * 3 * 16 + 1) * 32
Out[ ]:
4640
In [ ]:
"""
기본적인 CNN 구조러 설계한 네트워크의 학습
참고 ) 장답을 분류 ---> 원핫 인코딩 FM
: 너무 너무 귀찮다...
loss 함수를 지정하는 과정에서 앞에 spase ~~
굳이 정답을 원핫인코딩을 안하고, 라벨인코딩으로 넣어도 됨!
참고 ) 여거서는 정답지 (라벨 인코딩 된거 그대로 사용)
"""
Out[ ]:
'\n기본적인 CNN 구조러 설계한 네트워크의 학습\n참고 ) 장답을 분류 ---> 원핫 인코딩 FM\n : 너무 너무 귀찮다...\nloss 함수를 지정하는 과정에서 앞에 spase ~~\n굳이 정답을 원핫인코딩을 안하고, 라벨인코딩으로 넣어도 됨!\n\n참고 ) 여거서는 정답지 (라벨 인코딩 된거 그대로 사용)\n'
In [ ]:
train_y
Out[ ]:
array([9, 0, 0, ..., 3, 0, 5], dtype=uint8)
In [ ]:
model.compile(
loss = "sparse_categorical_crossentropy",
optimizer = tf.keras.optimizers.Adam(),
metrics=["accuracy"]
)
In [ ]:
# 실제 학습을 진행을 하면됨
history = model.fit(
train_X, train_y,
# 라벨 인코딩왼 정답지를 사용 가능한 이유 : loss에 sparse를 써 붙여서
epochs = 20,
validation_split = 0.25,
batch_size = 128
)
Epoch 1/20 352/352 [==============================] - 5s 11ms/step - loss: 2.3026 - accuracy: 0.0997 - val_loss: 2.3026 - val_accuracy: 0.1009 Epoch 2/20 352/352 [==============================] - 4s 10ms/step - loss: 2.3026 - accuracy: 0.0997 - val_loss: 2.3026 - val_accuracy: 0.1009 Epoch 3/20 352/352 [==============================] - 4s 11ms/step - loss: 2.3026 - accuracy: 0.0997 - val_loss: 2.3026 - val_accuracy: 0.1009 Epoch 4/20 352/352 [==============================] - 4s 10ms/step - loss: 2.3026 - accuracy: 0.0997 - val_loss: 2.3026 - val_accuracy: 0.1009 Epoch 5/20 352/352 [==============================] - 4s 11ms/step - loss: 2.3026 - accuracy: 0.0997 - val_loss: 2.3026 - val_accuracy: 0.1009 Epoch 6/20 352/352 [==============================] - 4s 12ms/step - loss: 2.3026 - accuracy: 0.0997 - val_loss: 2.3026 - val_accuracy: 0.1009 Epoch 7/20 352/352 [==============================] - 4s 10ms/step - loss: 2.3026 - accuracy: 0.0997 - val_loss: 2.3026 - val_accuracy: 0.1009 Epoch 8/20 352/352 [==============================] - 4s 11ms/step - loss: 2.3026 - accuracy: 0.0997 - val_loss: 2.3026 - val_accuracy: 0.1009 Epoch 9/20 352/352 [==============================] - 4s 12ms/step - loss: 2.3026 - accuracy: 0.0997 - val_loss: 2.3026 - val_accuracy: 0.1009 Epoch 10/20 352/352 [==============================] - 4s 11ms/step - loss: 2.3026 - accuracy: 0.0997 - val_loss: 2.3026 - val_accuracy: 0.1009 Epoch 11/20 352/352 [==============================] - 4s 11ms/step - loss: 2.3026 - accuracy: 0.0997 - val_loss: 2.3026 - val_accuracy: 0.1009 Epoch 12/20 352/352 [==============================] - 4s 11ms/step - loss: 2.3026 - accuracy: 0.0997 - val_loss: 2.3026 - val_accuracy: 0.1009 Epoch 13/20 352/352 [==============================] - 4s 11ms/step - loss: 2.3026 - accuracy: 0.0997 - val_loss: 2.3026 - val_accuracy: 0.1009 Epoch 14/20 352/352 [==============================] - 4s 11ms/step - loss: 2.3026 - accuracy: 0.0997 - val_loss: 2.3026 - val_accuracy: 0.1009 Epoch 15/20 352/352 [==============================] - 4s 11ms/step - loss: 2.3026 - accuracy: 0.0997 - val_loss: 2.3026 - val_accuracy: 0.1009 Epoch 16/20 352/352 [==============================] - 4s 12ms/step - loss: 2.3026 - accuracy: 0.0997 - val_loss: 2.3026 - val_accuracy: 0.1009 Epoch 17/20 352/352 [==============================] - 4s 10ms/step - loss: 2.3026 - accuracy: 0.0997 - val_loss: 2.3026 - val_accuracy: 0.1009 Epoch 18/20 352/352 [==============================] - 4s 11ms/step - loss: 2.3026 - accuracy: 0.0997 - val_loss: 2.3026 - val_accuracy: 0.1009 Epoch 19/20 352/352 [==============================] - 4s 12ms/step - loss: 2.3026 - accuracy: 0.0997 - val_loss: 2.3026 - val_accuracy: 0.1009 Epoch 20/20 352/352 [==============================] - 4s 12ms/step - loss: 2.3026 - accuracy: 0.0997 - val_loss: 2.3026 - val_accuracy: 0.1009
In [ ]:
# 해당하는 결과는 상황마다 조금 다르지만,,
# 대략적으로 overfit에 걸리는 상황이므로
# 이 부분을 해결하기 위한 초기 버전들의 해결책!!!!
# ==> 연결을 줄이자!!!! & 대표화!!!!!
# conv : stride (샘플링에 대한 빈도...)
# pooling : 대표화 (FM에 대한 대표화)
# ++ 분류쪽에는 DNN dropout 활용해서 연결을 끊어내서
# ===> OverFit 줄이고자 함!!!!
# 초기 CNN : Conv + pool + Conv + Pool....
# VGG 논문 이후 : Conv+Conv+Conv+Pool etc
In [ ]:
# 시도1) 초기 버전으로 시도...
model = tf.keras.Sequential([
tf.keras.layers.Conv2D( input_shape=(28,28,1),kernel_size=(3,3),filters = 16),
tf.keras.layers.MaxPooling2D( pool_size=(2,2), strides=(2,2)),
tf.keras.layers.Conv2D(kernel_size=(3,3), filters=32),
tf.keras.layers.MaxPooling2D( pool_size=(2,2), strides=(2,2)),
tf.keras.layers.Conv2D(kernel_size=(3,3), filters=64),
tf.keras.layers.Flatten(),
tf.keras.layers.Dropout( rate= 0.3),
tf.keras.layers.Dense( units = 128, activation="relu"),
tf.keras.layers.Dropout( rate= 0.3),
tf.keras.layers.Dense( units = 10, activation="softmax")
])
model
In [ ]:
model.summary()
Model: "sequential" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= conv2d_1 (Conv2D) (None, 26, 26, 16) 160 conv2d_2 (Conv2D) (None, 24, 24, 32) 4640 conv2d_3 (Conv2D) (None, 22, 22, 64) 18496 flatten (Flatten) (None, 30976) 0 dense (Dense) (None, 126) 3903102 dense_1 (Dense) (None, 10) 1270 ================================================================= Total params: 3927668 (14.98 MB) Trainable params: 3927668 (14.98 MB) Non-trainable params: 0 (0.00 Byte) _________________________________________________________________
In [ ]:
# 동일한 구조인데 대표화& 중간에 짜르기
# ==> 400만개 파라미터 --> 10만개 정도로 줄이는 것!!!!
# 내가 어떻게 설계하는지에 따라서 엄청나가 다양성이 존재!!!!!
# ===> 실험의 영역임;;;
In [ ]:
model.compile(
loss = "sparse_categorical_crossentropy",
optimizer = tf.keras.optimizers.Adam(),
metrics = ["accuracy"]
File "<ipython-input-49-436651526543>", line 4 metrics = ["accuracy"] ^ SyntaxError: incomplete input
In [ ]:
history = model.fit(
train_X, train_y,
epochs = 20,
validation_split =0.25,
batch_size= 128
In [ ]:
#### 모델링에서 중요한 기본적인 부분은 Bias-Variance
# ==> UnderFit ~~~ 적당한fit ~~~~ overFit
# 기존의 ML ) HPT을 중심으로 해당하는 모델의 학습을
# max_depth etc
# Deep Learning ) 모델의 구조를 통해서 조절!!!!!!!
# : 연결성을 중심으로 처리를 함!!!!!
# ///////
In [ ]:
# ---> 모델을 경량화를 했더니....학습이 더 잘된다!!!
# 더 학습을 해도 될 것 같다......
# 지금까지는 OverFit은 아닌거 같으니,,더 해보고 싶다....
# callback 을 사용해서......모니터링을 해야함!!!!!
# + 중간 중간 모델의 weight를 wwjwkd
In [ ]:
import os
cp_path = "training/cp-{epoch:04d}.ckpt"
cp_dir = os.path.dirname(cp_path) # training
cp_callback = tf.keras.callbacks.ModelCheckpoint(
cp_path,
verbose = 1,
save_weights_only = True
)
es_callback = tf.keras.callbacks.EarlyStopping(
monitor='val_loss',
patience = 10 # 참을 횟수... 갱신이 안되는 횟수
)
history = model.fit( train_X, train_y,
epochs = 200,
validation_split= 0.25,
batch_size=128,
callbacks=[cp_callback, es_callback])
Epoch 1/200 351/352 [============================>.] - ETA: 0s - loss: 2.3026 - accuracy: 0.0997 Epoch 1: saving model to training/cp-0001.ckpt 352/352 [==============================] - 4s 12ms/step - loss: 2.3026 - accuracy: 0.0997 - val_loss: 2.3026 - val_accuracy: 0.1009 Epoch 2/200 349/352 [============================>.] - ETA: 0s - loss: 2.3026 - accuracy: 0.0996 Epoch 2: saving model to training/cp-0002.ckpt 352/352 [==============================] - 4s 11ms/step - loss: 2.3026 - accuracy: 0.0997 - val_loss: 2.3026 - val_accuracy: 0.1009 Epoch 3/200 348/352 [============================>.] - ETA: 0s - loss: 2.3026 - accuracy: 0.0995 Epoch 3: saving model to training/cp-0003.ckpt 352/352 [==============================] - 4s 10ms/step - loss: 2.3026 - accuracy: 0.0997 - val_loss: 2.3026 - val_accuracy: 0.1009 Epoch 4/200 347/352 [============================>.] - ETA: 0s - loss: 2.3026 - accuracy: 0.0994 Epoch 4: saving model to training/cp-0004.ckpt 352/352 [==============================] - 4s 10ms/step - loss: 2.3026 - accuracy: 0.0997 - val_loss: 2.3026 - val_accuracy: 0.1009 Epoch 5/200 349/352 [============================>.] - ETA: 0s - loss: 2.3026 - accuracy: 0.0998 Epoch 5: saving model to training/cp-0005.ckpt 352/352 [==============================] - 4s 11ms/step - loss: 2.3026 - accuracy: 0.0997 - val_loss: 2.3026 - val_accuracy: 0.1009 Epoch 6/200 347/352 [============================>.] - ETA: 0s - loss: 2.3026 - accuracy: 0.0996 Epoch 6: saving model to training/cp-0006.ckpt 352/352 [==============================] - 4s 11ms/step - loss: 2.3026 - accuracy: 0.0997 - val_loss: 2.3026 - val_accuracy: 0.1009 Epoch 7/200 347/352 [============================>.] - ETA: 0s - loss: 2.3026 - accuracy: 0.0997 Epoch 7: saving model to training/cp-0007.ckpt 352/352 [==============================] - 4s 11ms/step - loss: 2.3026 - accuracy: 0.0997 - val_loss: 2.3026 - val_accuracy: 0.1009 Epoch 8/200 350/352 [============================>.] - ETA: 0s - loss: 2.3026 - accuracy: 0.0995 Epoch 8: saving model to training/cp-0008.ckpt 352/352 [==============================] - 4s 11ms/step - loss: 2.3026 - accuracy: 0.0997 - val_loss: 2.3026 - val_accuracy: 0.1009 Epoch 9/200 348/352 [============================>.] - ETA: 0s - loss: 2.3026 - accuracy: 0.0998 Epoch 9: saving model to training/cp-0009.ckpt 352/352 [==============================] - 4s 10ms/step - loss: 2.3026 - accuracy: 0.0997 - val_loss: 2.3026 - val_accuracy: 0.1009 Epoch 10/200 349/352 [============================>.] - ETA: 0s - loss: 2.3026 - accuracy: 0.0999 Epoch 10: saving model to training/cp-0010.ckpt 352/352 [==============================] - 4s 11ms/step - loss: 2.3026 - accuracy: 0.0997 - val_loss: 2.3026 - val_accuracy: 0.1009 Epoch 11/200 351/352 [============================>.] - ETA: 0s - loss: 2.3026 - accuracy: 0.0997 Epoch 11: saving model to training/cp-0011.ckpt 352/352 [==============================] - 4s 11ms/step - loss: 2.3026 - accuracy: 0.0997 - val_loss: 2.3026 - val_accuracy: 0.1009
In [ ]:
# 참고) 위에서는 처음 20에포크 돌린 이후에 200세팅이여서
# 시작점이 높이 시작을 함!!!! 0.9X
In [60]:
# 앞에서 학습한 결과들을 바탕으로 제일 좋을것 같은
# 모델의 weights를 가지고 오겠습니다
# ==> 상황마다 다르기에 ... 스스로 결정해야 한다.
# 주의사항 ) 폴더에 보이는 파일명이 아니라 내가 저장한 양식대로!!!
load_cp_weights = "training/cp-0009.ckpt"
# 모델의 골조 :model이 변수 ..,. weights 담겨는 있음
# 구조는 동일하니, 안에 담겨는 있음
# 구조는 동일하나 안에 인테리어만 싹 가는것
# 참고 다른사름이 사용할떄는 모델 구조도 전달을 해야함
model.load_weights(load_cp_weights)
# 실제 평가
model.evaluate( test_X, test_y)
# 정답지를 그냥 활용할 수 있는 이유는
# compile에서 loss spares로 설정을 해서임!!!
#
313/313 [==============================] - 1s 3ms/step - loss: 2.3026 - accuracy: 0.1000
Out[60]:
[2.30259108543396, 0.10000000149011612]
In [61]:
## 대략 구조는 파악을 하고,,,
#### 완전히 최적을 해야한다면,,,HPT을 한다면,,
#### 구조적인 부분에 대한 여러 실험들을 해야함!!!!!!!
# + 필터의 수, 필터의 사이즈, 레이어의 수 etc
# ===> optuna로 실험을 할 수 있음!!!!!!!!!!!!!
# 단, 시간이 엄청 걸리는 부분!!!!
In [61]:
In [ ]:
"""
개선 방향 (논문, 리포트 분석 : 눈문대략적인 것들을 파악)
-> 잘 되는 모델들을 조사해서 ...가져다가 사용하자!!!
++ 데이터를 보강하자!!
---> 잘 되는 모델을 가져다가 사용하자 !!! (조금은 튜닝하자)
"""
In [62]:
"""# +++ CNN ----> VGG ---> etc.....
# CNN : conv + pool
# vgg : conv+conv+conv+pool
"""
Out[62]:
'# +++ CNN ----> VGG ---> etc.....\n# CNN : conv + pool\n# vgg : conv+conv+conv+pool\n\n'
In [ ]:
In [ ]:
In [ ]:
In [ ]:
In [ ]:
In [ ]:
In [ ]: