BLOG

GTEC & SMC & 339
[개발] 담배 피는 사람 분류 (합성곱 신경망)
A A
728x90
반응형
728x90

tensorflow로 합성곱 신경망을 구현하여 담배를 피는사람을 분류해보겠습니다.
https://www.kaggle.com/datasets/vitaminc/cigarette-smoker-detection

 

Cigarette Smoker Detection

Can you detect people smoking cigarette in an image?

www.kaggle.com

위 데이터셋을 사용하여 모델을 훈련시켰습니다.


데이터 로드

image_dataset_from_directory( )

image_dataset_from_directory 함수는 디렉토리에서 이미지 데이터셋을 생성하는 유틸리티 함수입니다. 이 함수를 사용하면 이미지 데이터셋을 쉽게 로드하고 전처리할 수 있습니다.

image_folder = "folder_location" 

# 데이터셋 생성
dataset = tf.keras.preprocessing.image_dataset_from_directory(
    image_folder,
    image_size=(224, 224),  # 이미지 크기
    batch_size=32,  # 배치 크기
    shuffle=True,  # 데이터를 섞을지 여부
    seed=123,  # 섞기 위한 시드 값
    validation_split=0.2,  # 검증 데이터셋의 비율
    subset="training"  # 훈련 데이터셋의 비율
)

여기서 중요한 점은 not_smoking과 smoking을 따로 불러와 객체를 생성할 필요 없이.
이를 모두 포함하고 있는 폴더를 선택해야합니다.
각 이미지에 상응하는 label은 그 이미지들을 담고 있는 폴더 됩니다. 

     

전처리

normalized_dataset = dataset.map(lambda x, y: (x / 255.0, y))

각 픽셀 값은 0부터 254 까지의 값을 가집니다. 
모델의 학습을 원활하게 진행시키기 위해 모든 픽셀 값들을 각각 255로 나누어 정규화 시켜줍니다.

 

테스트, 검증 분활

train_dataset = normalized_dataset.take(int(len(dataset) * 0.8))
val_dataset = normalized_dataset.skip(int(len(dataset) * 0.8))

take와 skip 함수를 사용하여 기존 데이터셋의 80%는 학습 데이터 셋으로, 나머지 20%는 검증 데이터셋으로 사용됩니다.


모델 구현 

model = tf.keras.Sequential([
                      # (필터 개수, 커널의 행열, 패딩, 활성화 함수, 입력 데이터(첫 계층만 정의))
    tf.keras.layers.Conv2D(32, (3,3), padding = 'same', activation='relu', input_shape=(224,224,3)),
    tf.keras.layers.MaxPooling2D((2,2), strides=2), # 풀링층 필터 크기(2,2), 스트라이드 2
    tf.keras.layers.Conv2D(64, (3,3), padding = 'same', activation='relu'),
    tf.keras.layers.MaxPooling2D((2,2), strides=2),
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(128, activation='relu'),
    tf.keras.layers.Dense(128, activation='softmax')
])

model.compile(optimizer='adam',
                        loss='sparse_categorical_crossentropy',
                        metrics=['accuracy'])

모델은 총 2개의 합성곱층과 2개의 풀링층, 그리고 완전 연결층 및 출력층으로 이루어져있습니다.
저의 노트북 생명을 위해 아쉽게도 깊은 신경망을 구현하지는 않았습니다 ( 너무 무서워요 )
 
합성곱층을 통해 각 영역에 필터를 씌워가며 연산을 수행합니다 
이렇게 크기가 줄어든 픽셀 값들은 풀링층을 통해 의미 있는 값( ex최댓값 )을 추출하고 
완전 연결층으로 기본적인 딥러닝 연산을 진행합니다.
 
합성곱의 패딩값을 same으로 설정하여 입력 이미지와 출력 이미지의 크기를 동일하게 유지시키고
풀링층의 스트라이드를 2로 설정하여 각 필터를 씌울 범위를 지정합니다.


경사하강법의 영향을 주는 옵티마이저는 'adam'으로 설정하여 모멘텀의 장점(지역 최소점 벗어남)을 얻고 수렴속도를 빠르게 진행할 수 있습니다.
손실 함수는 'sparse_categorical_crossentropy'로 다중 클래스 분류에 사용됩니다.

 

모델 학습

model.fit(train_dataset, validation_data=val_dataset, epochs=10, batch_size=32)

train_dataset에 label 값이 이미 있기 때문에 따로 정답 데이터를 알려주지 않았습니다.
그리고 학습과 검증을 병행해서 하기 위해 validation_data 인자에 따라 위에서 만들었던 검정 데이터셋을 넣어줍니다.
에포크는 간단하게 10번만 돌려보겠습니다.
 
약 54분 후.....
 
생각보다 학습이 너무 잘 돼서 놀랐습니다. 첫 술에 배부른 느낌이네요
시각화도 해보고 싶지만... 콜백 함수를 만들어 놓지 않아서 늦어버렸네요 ㅠㅠ  


테스트(가장 설레는순간)

테슷투를 한번 해보겠습니다.
구글에서 해당 사진을 구해봤습니다.
 

위에 세 사진은 담배를 피고있지 않은 사람 한명과 담배와 약간 비슷해 보이는 연필, 기둥 사진입니다.
각각 이름을 ns_0 ns_1 ns_2 으로 지정했습니다.
 

위의 사진들은 누가 봐도 담배 피는 사람들이죠?
각각 이름은 s_0 s_1 s_2 s_3으로 지정했습니다.

 

예측

from tensorflow.keras.preprocessing.image import load_img
image = load_img('testImg/사진명.jpg', target_size=(224, 224))

이제 이미지 예측을 하기 위해 텐서플로우의 내장함수인 load_img 함수를 사용하겠습니다.
target_size로 모델이 학습할 때 입력 받았던 이미지 모양 그대로 불러옵니다.
 
바로 시작... 을 하면 안됩니다. 왜나하면 이미지를 배열 형태로 바꿔야 하기 때문입니다.

from tensorflow.keras.preprocessing.image import img_to_array
image = img_to_array(image)
image = tf.expand_dims(image, 0)

만들어진 배열은 tf.expand_dims(image, 0)로 image 텐서의 차원을 확장하여 배치 차원을 추가한 결과로 바꿔줍니다.
모델은 이미지를 입력할때 4차원의 축으로 감싸여진 각각의 개체로 인식합니다(?) 
 


predictions = model.predict(image)

 

이렇게 전처리된 테스트용 이미지를 predict 함수의 값으로 넣어줍니다.
 

 

그러면 이러한 형태의 결과를 얻을 수 있습니다.
처음 봤을 때는 이미지의 픽셀값을 모조리 0으로 바꿔버리고 무작위로 1 값을 넣은줄 알았습니다.
하지만 값의 위치 범위가 0 아니면 1이었습니다.
 
생각해보니 저는 label 명을 따로 설정해 준 적이 없습니다. 따라서 모델은
담배를 안피는 이미지는 1의 위치를 0
담배를 피는 이미지는 1의 위치를 1에 배정합니다.

np.argmax(predictions, axis=1)

이렇게 하니 직관적으로 안피는지(0) 피는지(1)를 알 수 있네요 ㅎㅎ
 
수정 - 출력층(softmax)의 뉴런을 128개로 지정했기 때문에 출력값의 범위가 128개가 됐었네요!!
각 뉴런은 입력 이미지가 해당 클래스에 속할 확률을 출력해줍니다.


ns_0 부터 ns_2 이미지는 모두 안핀다(0)의 값이 나왔습니다.
그러나
s_0, s_1과 s_3는 모두 핀다(1)의 값이 나왔지만
s_2 이미지가 안핀다(0)의 값이 나왔습니다.
아마 모델 훈련에 사용한 이미지들이 대부분 외국인들이 담배를 피는사진들이라 그런 것 같습니다...

따라서 다음에는 객체 인식을 통해 담배가 들어간 이미지를 분류해볼 것입니다.

728x90
반응형
Copyright 2025. GRAVITY all rights reserved