← Back to portfolio
Project · CNN

GPR 데이터를 이용한 매설물 탐지모델

ToyProjectCNN · ResNet데이터 전처리 · 모델링

🪢 데이터 출처

https://github.com/rpl-cmu/CMU-GPR-Dataset

📌프로젝트 개요

1. 데이터 확인

→ 뭐가 많음… 이게 뭔지 확인 !

8 rows × 201 columns

📌 200개의 반사된 주파수의 “시간별 진폭”이 저장된 것.
0번째 시간의 진폭, 1번째 시간의 진폭… 200번째 시간의 진폭

df[['Amp_0','Amp_10', 'Amp_100']].plot(grid='on')

Untitled

→ 주황색(10): 급격하게 떨어지는 부분 있음. 지하에 뭔가 있다는 의미!

→ 초록색(100), 파란색(0): 미미하지만 파동이 달라지고 있음.

⇒ 시간에 따른 값을 표기할 수 있으니 이걸 이미지로 변환할 수 있을 것. (초음파 사진처럼)

➡️ 1. df 변환
df.values: 전체 컬럼을 matrix로 변환
→ y축이 시간임. 아래로 길어서 보기 힘들듯.
df.values.T로 가로로기이이이이이이일게 만들기

➡️ 2. 정규화: min-max normalization

(vmax, vmin) = (2500, -1000)
norm_img = (image_like_data - vmin)/(vmax-vmin)
# -> 이렇게만 진행하면 최대/최소값을 넘어가는 애들도 있어서 
# np.clip으로 1로 만들어주기
clipped_img = np.clip(0,1, norm_img)

➡️ 3. 이미지로 변환해서 출력해보기

Untitled

💡 영상 분류를 위한 레이블링 이미지셋 → CNN 모델 학습 → 평가

2. CNN 모델 설계

abnormal GPR 100장, normal GPR 100장이라서, 아무리 토이 프로젝트라 해도 이미지가 적기 때문에 augmentation으로 양을 늘려주려고 함.

data_augmentation = keras.Sequential(
    [
        layers.RandomFlip(mode='horizontal'),
    ]
)
def make_model(input_shape, num_classes):
    inputs = keras.Input(shape=input_shape)

    # 이미지 augmentation 설정
    x = data_augmentation(inputs)

    # 초기 레이어 설정
    x = layers.Rescaling(1.0 / 255)(x)
    x = layers.Conv2D(32, 3, strides=2, padding="same")(x)
    x = layers.BatchNormalization()(x)
    x = layers.Activation("relu")(x)

    x = layers.Conv2D(64, 3, padding="same")(x)
    x = layers.BatchNormalization()(x)
    x = layers.Activation("relu")(x)

    previous_block_activation = x  # residual 설정 

    for size in [8, 16, 32, 48]:
        x = layers.Activation("relu")(x)
        x = layers.SeparableConv2D(size, 3, padding="same")(x)
        x = layers.BatchNormalization()(x)

        x = layers.Activation("relu")(x)
        x = layers.SeparableConv2D(size, 3, padding="same")(x)
        x = layers.BatchNormalization()(x)

        x = layers.MaxPooling2D(3, strides=2, padding="same")(x)

        # Project residual
        residual = layers.Conv2D(size, 1, strides=2, padding="same")(
            previous_block_activation
        )
        x = layers.add([x, residual]) # back residual 설정 
        previous_block_activation = x # next residual 설정 

    x = layers.SeparableConv2D(128, 3, padding="same")(x)
    x = layers.BatchNormalization()(x)
    x = layers.Activation("relu")(x)

    x = layers.GlobalAveragePooling2D()(x)

    if num_classes == 2:
        activation = "sigmoid"
        units = 1
    else:
        activation = "softmax"
        units = num_classes

    x = layers.Dropout(0.5)(x)
    outputs = layers.Dense(units, activation=activation)(x)

    return keras.Model(inputs, outputs)

model = make_model(input_shape=image_size + (3,), num_classes=2)
keras.utils.plot_model(model, show_shapes=True)

3. 모델 학습

!ls data/labeled

abnormal normal

→ abnoraml, normal 있는 것 확인

💡 tf.keras.preprocessing.image_dataset_from_directory()
: 클래스별 폴더가 있을 경우 데이터셋 만드는 메서드

train_ds = tf.keras.preprocessing.image_dataset_from_directory(
    "./data/labeled",
    validation_split=0.1,
    subset="training",
    seed=seed,
    image_size=image_size,
    batch_size=batch_size,
)

val_ds = tf.keras.preprocessing.image_dataset_from_directory(
    "./data/labeled",
    validation_split=0.1,
    subset="validation",
    seed=seed,
    image_size=image_size,
    batch_size=batch_size,
)

Found 200 files belonging to 2 classes.
Using 180 files for training.
Found 200 files belonging to 2 classes.
Using 20 files for validation.

Untitled

0: 정상이다 1: 뭔가 있다

모델 학습시키기

epochs = 30

# callback 먼저 정의하기
callbacks = [
        # 가장 결과가 좋은 모델을 best.h5로 저장하기
    keras.callbacks.ModelCheckpoint("./models/best.h5", save_best_only=True, monitor='val_loss'),
    # 학습 과정에서 개선이 없으면 lr 조정하기 
    keras.callbacks.ReduceLROnPlateau(monitor='val_loss', factor=0.1, patience=10, verbose=0, mode='auto', min_delta=0.0001, cooldown=0, min_lr=0),
]

# compile
model.compile(
        optimizer=keras.optimizers.Adam(1e-3),
        loss = 'binary_crossentropy',
        metrics = ['accuracy'],
)

# 모델 학습
model.fit(
            train_ds, epochs = epochs, callbacks = callbacks, validation_data = val_ds,)

4. 모델 추론

학습된 모델 파일을 로드해서 샘플 이미지를 추론하기

: 학습을 통해 만들어진 모델을 → 실제로 새로운 입력 데이터에 적용하여 결과를 내놓는 단계

💡 1. 가장 학습 결과 좋은 모델 불러오기
2. 정상 이미지, 비정상이미지 불러오기
3. 이미지를 predict를 수행해서 확인

1/1 [==============================] - 0s 271ms/step
This image is 0.57 percent abnormal and 99.43 normal.
1/1 [==============================] - 0s 23ms/step
This image is 99.97 percent abnormal and 0.03 normal.

Untitled

Untitled

💡 normal 결과: abnormal 0.57, normal 99.43
abnormal 결과: abnoraml 99.97, noraml 0.03
→ 잘 분류 됨.

4. ResNet 모델

from tensorflow import Tensor
from tensorflow.keras.layers import Input, Conv2D, ReLU, BatchNormalization, AveragePooling2D, Flatten, Dense
from tensorflow.keras.models import Model

def relu_bn(inputs: Tensor) -> Tensor:
    relu = ReLU()(inputs)
    bn = BatchNormalization()(relu)
    return bn

def residual_block(x: Tensor, downsample: bool, filters: int, kernel_size: int = 3) -> Tensor:
    y = Conv2D(kernel_size=kernel_size,
               strides= (1 if not downsample else 2),
               filters=filters,
               padding="same")(x)
    y = relu_bn(y)
    y = Conv2D(kernel_size=kernel_size,
               strides=1,
               filters=filters,
               padding="same")(y)

    if downsample:
        x = Conv2D(kernel_size=1,
                   strides=2,
                   filters=filters,
                   padding="same")(x)
    out = layers.add([x, y])
    out = relu_bn(out)
    return out

def make_resnet(input_shape, num_classes):
    num_filters = 4
    inputs = keras.Input(shape=input_shape)
    # Image augmentation block
    x = data_augmentation(inputs)

    # Entry block
    x = layers.Rescaling(1.0 / 255)(x)

    x = BatchNormalization()(x)
    x = Conv2D(kernel_size=3,
               strides=1,
               filters=num_filters,
               padding="same")(x)
    x = relu_bn(x)

    num_blocks_list = [2, 5, 5, 2]
    for i in range(len(num_blocks_list)):
        num_blocks = num_blocks_list[i]
        for j in range(num_blocks):
            x = residual_block(x, downsample=(j==0 and i!=0), filters=num_filters)
        num_filters *= 2

    x = layers.GlobalAveragePooling2D()(x)
    if num_classes == 2:
        activation = "sigmoid"
        units = 1
    else:
        activation = "softmax"
        units = num_classes

    x = layers.Dropout(0.5)(x)
    outputs = layers.Dense(units, activation=activation)(x)
    return keras.Model(inputs, outputs)

resnet_model = make_resnet(input_shape=image_size + (3,), num_classes=2)
epochs = 20
callbacks = [
    keras.callbacks.ModelCheckpoint("./models/resnet_best.h5", save_best_only=True, monitor='val_loss'),
    keras.callbacks.ReduceLROnPlateau(monitor='val_loss', factor=0.1, patience=5, verbose=0, mode='auto', min_delta=0.00001, cooldown=0, min_lr=0),
]

resnet_model.compile(
    optimizer=keras.optimizers.Adam(1e-3),
    loss="binary_crossentropy",
    metrics=["accuracy"],
)

resnet_model.fit(
    train_ds,
    epochs=epochs,
    callbacks=callbacks,
    validation_data=val_ds,
)

같은 방법으로 결과 제일 좋은거 모델 불러오고, 정상+비정상 이미지 불러오고, 이미지들을 가장 좋은 모델에 넣어 predict를 수행해서 score확인

resnet_model = tf.keras.models.load_model("./models/resnet_best.h5")
normal_img = keras.preprocessing.image.load_img(
    "./data/labeled/normal/1613059433_516002_X_2.3793_Y_-35.1849_T_odom_20.8377_dir_-1.0_0.png", target_size=image_size
)
abnormal_img = keras.preprocessing.image.load_img(
"./data/labeled/abnormal/1613059614_8893247_X_12.9562_Y_-44.8245_T_odom_35.5064_dir_-1.0_0.png", target_size=image_size
)

def predict_and_show(img):
    img_array = keras.preprocessing.image.img_to_array(img)
    img_array = tf.expand_dims(img_array, 0)  # Create batch axis
    predictions = resnet_model.predict(img_array)
    score = predictions[0]
    print( "This image is %.2f percent abnormal and %.2f normal." % (100 * (1 - score), 100 * score))
    plt.figure()
    plt.imshow(img, cmap='gray')

predict_and_show(normal_img)
predict_and_show(abnormal_img)

1/1 [==============================] - 0s 450ms/step
This image is 4.07 percent abnormal and 95.93 normal.
1/1 [==============================] - 0s 30ms/step
This image is 93.03 percent abnormal and 6.97 normal.

Untitled

Untitled

Untitled

선의 굵기를 보면 미세하게 달라짐. abnormal로 비교해보면 ResNet이 더 뚜렷하게 구분하는 것을 확인함.

💡깨달은 점