RandomForestClassifier는 앙상블 학습(Ensemble Learning) 기법 중 하나인 랜덤 포레스트(Random Forest)를 구현한 분류(Classification) 모델입니다. 랜덤 포레스트는 여러 개의 의사결정 트리(Decision Tree)를 결합하여 강력한 예측 모델을 만드는 알고리즘입니다.
< 랜덤 포레스트 특징 >
앙상블 학습: 랜덤 포레스트는 여러 개의 의사결정 트리를 동시에 학습하고 이들의 예측 결과를 결합하여 최종 예측을 수행합니다. 이렇게 여러 개의 모델을 결합하는 앙상블 학습은 일반적으로 단일 모델보다 더욱 강력한 예측 성능을 제공합니다. 의사결정 트리: 랜덤 포레스트의 기본 모델로 사용되는 의사결정 트리는 데이터의 특성을 기반으로 하여 분류 작업을 수행하는 모델입니다. 의사결정 트리는 특정 기준에 따라 데이터를 분할하여 예측을 수행하는 방식으로 동작합니다. 랜덤성: 랜덤 포레스트는 의사결정 트리 학습 시에 랜덤성을 도입합니다. 이는 데이터의 일부 특성을 임의로 선택하거나 데이터를 부트스트랩 샘플링하는 등의 방법으로 랜덤성을 추가함으로써 모델의 다양성을 높입니다. 이는 과적합(Overfitting)을 방지하고 모델의 일반화 성능을 향상시킵니다. 랜덤 포레스트는 다양한 분야에서 활용되며, 데이터 분류, 패턴 인식, 텍스트 분석, 이미지 분류 등 다양한 문제에 적용될 수 있습니다. 또한, 특성 중요도를 제공하여 어떤 특성이 예측에 가장 중요한 역할을 하는지를 확인할 수도 있습니다.
//
// MNIST - RANDOMFORESTCLASSIFIER
//
// Created by netcanis on 2023/07/20.
//
import cv2
import os
import numpy as np
import pickle
import dataset_loader
import model_tester
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.svm import SVC
# Load MNIST data
training_images, training_labels, test_images, test_labels = dataset_loader.load_dataset("data/MNIST")
# Reshape the data
# 배열의 차원을 변경하여 크기를 자동으로 변경한다. (-1은 해당 차원의 크기를 자동으로 조정하라는 뜻)
training_images = np.reshape(training_images, (len(training_images), -1))
test_images = np.reshape(test_images, (len(test_images), -1))
# Print the image shapes
# reshape - Training Images shape: (60000, 784)
# reshape - Test Images shape: (10000, 784)
print("reshape - Training Images shape:", training_images.shape)
print("reshape - Test Images shape:", test_images.shape)
# Assign images and labels to x_train, y_train, x_test, y_test
x_train, y_train = training_images, training_labels
x_test, y_test = test_images, test_labels
#
# RandomForestClassifier 파일 저장.
#
# Train the model
model = RandomForestClassifier()
model.fit(x_train, y_train)
# Evaluate the model
score = model.score(x_test, y_test)
print('Accuracy:', score)
# Save the pkl model
with open("rfc_mnist_model.pkl", "wb") as file:
pickle.dump(model, file)
print("Save the rfc_mnist_model.pkl.")
#
# TEST
#
model_file = "rfc_mnist_model.pkl"
model_tester.test_model("data/MNIST", model_file)
# Error rate: 3.09%
SVC(Support Vector Classifier)는 SVM(Support Vector Machine) 알고리즘을 기반으로 한 분류(Classification) 모델입니다. 서포트 벡터 머신은 지도 학습(Supervised Learning)에서 주로 사용되며, 데이터를 분류하는 경계를 찾는 데에 특화되어 있습니다.
< SVC 특징 >
최대 마진 분류: SVC는 데이터를 분류하는 결정 경계를 찾을 때, 가능한 한 최대 마진(Margin)을 가지도록 합니다. 마진은 결정 경계와 가장 가까운 데이터 샘플들 간의 거리를 의미하며, 이를 최대화함으로써 일반화 성능을 향상시킬 수 있습니다. 커널 기법: SVC는 비선형적인 데이터를 처리하기 위해 커널(Kernel) 기법을 사용합니다. 커널은 데이터를 고차원 특징 공간으로 매핑하여 선형적으로 분리할 수 있도록 합니다. 대표적인 커널 함수로는 선형 커널, 다항식 커널, 가우시안(RBF) 커널 등이 있습니다. 서포트 벡터: SVC는 분류 결정 경계에 가장 가까이 위치한 데이터 샘플들을 서포트 벡터(Support Vector)라고 부릅니다. 이들 데이터 샘플들은 분류 결정에 영향을 미치는 주요한 역할을 합니다. SVC는 이 서포트 벡터들을 효율적으로 찾아내어 분류를 수행합니다. SVC는 이진 분류(Binary Classification)와 다중 클래스 분류(Multi-Class Classification) 문제에 모두 사용될 수 있습니다. 또한, SVM은 분류 뿐만 아니라 회귀(Regression), 이상치 탐지(Outlier Detection), 차원 축소(Dimensionality Reduction) 등 다양한 문제에도 적용될 수 있습니다.
//
// MINST - SVC(SUPPORT VECTOR CLASSIFIER)
//
// Created by netcanis on 2023/07/20.
//
import numpy as np
import pickle
import dataset_loader
import model_tester
from sklearn.model_selection import train_test_split
from sklearn.svm import SVC
# Load MNIST data
training_images, training_labels, test_images, test_labels = dataset_loader.load_dataset("data/MNIST")
# Reshape the data
# 배열의 차원을 변경하여 크기를 자동으로 변경한다. (-1은 해당 차원의 크기를 자동으로 조정하라는 뜻)
training_images = np.reshape(training_images, (len(training_images), -1))
test_images = np.reshape(test_images, (len(test_images), -1))
# Print the image shapes
# reshape - Training Images shape: (60000, 784)
# reshape - Test Images shape: (10000, 784)
print("reshape - Training Images shape:", training_images.shape)
print("reshape - Test Images shape:", test_images.shape)
# Assign images and labels to x_train, y_train, x_test, y_test
x_train, y_train = training_images, training_labels
x_test, y_test = test_images, test_labels
#
# SVC(Support Vector Classifier) 파일 저장.
#
print("Training in progress... Please wait.")
# Train the model
# 'verbose=True'를 설정하여 진행상태 로그 출력.
model = SVC(verbose=True)
model.fit(x_train, y_train)
# Evaluate the model
score = model.score(x_test, y_test)
print('Accuracy:', score)
# Save the model (Support Vector Machines)
with open("svm_mnist_model.pkl", "wb") as file:
pickle.dump(model, file)
print("Save the vm_mnist_model.pkl.")
#
# TEST
#
model_file = "svm_mnist_model.pkl"
model_tester.test_model("data/MNIST", model_file)
# Error rate: 2.08%
//
// MNIST 모델 테스터
//
// Created by netcanis on 2023/07/20.
//
import cv2
import os
import numpy as np
import pickle
import tensorflow as tf
from keras.models import load_model
def test_model(path, model_file):
print("Testing in progress...")
# 데이터셋 경로
test_set_path = os.path.join(path, 'test_set')
total_samples = 0
error_count = 0
# Load the model from file
with open(model_file, "rb") as file:
if model_file.endswith('.h5'):
loaded_model = load_model(model_file)
elif model_file.endswith('.tflite'):
interpreter = tf.lite.Interpreter(model_path = model_file)
interpreter.allocate_tensors()
# Get the input and output details
input_details = interpreter.get_input_details()
output_details = interpreter.get_output_details()
else:
loaded_model = pickle.load(file)
# Load the images from the test set
for digit_folder in os.listdir(test_set_path):
if os.path.isdir(os.path.join(test_set_path, digit_folder)):
label = int(digit_folder)
for index, image_file in enumerate(os.listdir(os.path.join(test_set_path, digit_folder))):
if image_file.endswith('.png') or image_file.endswith('.jpg'):
image = cv2.imread(os.path.join(test_set_path, digit_folder, image_file))
if path.endswith('/credit_card'):
image = cv2.resize(image, (32, 32))
else: # '/MNIST'
image = cv2.resize(image, (28, 28))
# Convert color image to grayscale if necessary
if image.shape[2] > 1:
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
if model_file.endswith('.h5'):
image = image.reshape(1, 28, 28, 1) # 배열을 4차원으로 변경.
image = image / 255.0
# Predict the label for the image
predicted_label = loaded_model.predict(image)
# Get the predicted class
predicted_class = np.argmax(predicted_label)
elif model_file.endswith('.tflite'):
image = np.expand_dims(image, axis=0) # Add batch dimension
image = np.expand_dims(image, axis=3) # Add channel dimension
image = image.astype(np.float32) / 255.0
# Set the input tensor
interpreter.set_tensor(input_details[0]['index'], image)
# Run the inference
interpreter.invoke()
# Get the output tensor
output_tensor = interpreter.get_tensor(output_details[0]['index'])
# Get the predicted class
predicted_class = np.argmax(output_tensor)
else: # SVM, RandomForestClassifier ('.pkl')
# Reshape the data
image = image.reshape(1, -1)
# Predict the label for the image
predicted_label = loaded_model.predict(image)
# Get the predicted class
predicted_class = predicted_label[0]
# error
if predicted_class != label:
error_count += 1
print(f"Prediction for {index} - {label}: {predicted_class}")
total_samples += 1
# Print error rate
error_rate = (error_count / total_samples) * 100
print(f"Error rate: {error_rate:.2f}%")
사용 방법은 다음과 같다.
import mnist_model_tester
model_file = "svm_mnist_model.pkl"
mnist_model_tester.test_mnist_model("data/MNIST", model_file)
or
from mnist_model_tester import test_mnist_model
model_file = "svm_mnist_model.pkl"
test_mnist_model("data/MNIST", model_file)
data/MNIST/row/ 폴더에 다운로드된 데이터셋 정보를 이용하여 image파일로 복원하고 해당 숫자(0-9)별로 폴더를 생성하고 각 숫자에 맞게 폴더내에 저장하도록 구현해 보았다. labels.txt에는 각각의 이미지에대한 숫자 값을 기록하였다.
//
// MNIST 데이터셋을 이미지 파일로 복원
//
// Created by netcanis on 2023/07/20.
//
import matplotlib.pyplot as plt
import gzip
import numpy as np
import os
# 파일 압축 해제 함수
def decompress_file(compressed_file, decompressed_file):
with gzip.open(compressed_file, 'rb') as f_in, open(decompressed_file, 'wb') as f_out:
f_out.write(f_in.read())
'''
================= training_set =================
'''
# 이미지 개수 및 크기 추출
training_num_images = 60000 # 예시로는 60,000개의 이미지가 있는 것으로 가정
image_size = 28 # 이미지 크기는 28x28 픽셀
# 압축 해제할 파일 경로 (training_set)
training_image_compressed_file = 'data/MNIST/raw/train-images-idx3-ubyte.gz'
training_label_compressed_file = 'data/MNIST/raw/train-labels-idx1-ubyte.gz'
# 압축 해제된 파일 경로 (training_set)
training_image_decompressed_file = 'data/MNIST/raw/train-images-idx3-ubyte'
training_label_decompressed_file = 'data/MNIST/raw/train-labels-idx1-ubyte'
# 이미지 저장 폴더 경로
training_set_folder = 'data/MNIST/training_set'
# 라벨 저장 파일 경로
training_label_file = 'data/MNIST/training_set/labels.txt'
# 이미지 파일 압축 해제 (training_set)
decompress_file(training_image_compressed_file, training_image_decompressed_file)
# 라벨 파일 압축 해제 (training_set)
decompress_file(training_label_compressed_file, training_label_decompressed_file)
# 이미지 저장 폴더 생성 (training_set)
if not os.path.exists(training_set_folder):
os.makedirs(training_set_folder)
# 라벨 데이터를 numpy 배열로 변환 (training_set)
with open(training_label_decompressed_file, 'rb') as f:
# 헤더 부분은 건너뛰기
f.read(8)
# 라벨 데이터 읽기
buf = f.read()
# 라벨 배열 형태로 변환 (training_set)
training_labels = np.frombuffer(buf, dtype=np.uint8)
# 라벨 저장 파일 열기 (training_set)
with open(training_label_file, 'w') as f:
# 이미지에 대한 라벨 값을 순차적으로 저장
for i in range(training_num_images):
image_path = f'{training_set_folder}/{training_labels[i]}/image_{i}.png' # 이미지 파일 경로 (image_{i:05}.png)
label_value = training_labels[i] # 라벨 값
# 이미지 파일 경로와 라벨 값을 파일에 저장
f.write(f'{image_path}\t{label_value}\n')
# 이미지 데이터를 numpy 배열로 변환 (training_set)
with open(training_image_decompressed_file, 'rb') as f:
# 헤더 부분은 건너뛰기
f.read(16)
# 이미지 데이터 읽기
buf = f.read()
# 이미지 배열 형태로 변환
training_images = np.frombuffer(buf, dtype=np.uint8).reshape(training_num_images, image_size, image_size)
# 이미지를 순서대로 저장 (training_set)
for i, image in enumerate(training_images):
label_value = training_labels[i] # 라벨 값
# 해당 숫자의 폴더 생성
digit_folder = os.path.join(training_set_folder, str(label_value))
if not os.path.exists(digit_folder):
os.makedirs(digit_folder)
# 이미지 파일 경로
image_path = os.path.join(digit_folder, f'image_{i}.png')
plt.imsave(image_path, image, cmap='gray')
'''
================= test_set =================
'''
# 이미지 개수 및 크기 추출
test_num_images = 10000 # 예시로는 60,000개의 이미지가 있는 것으로 가정
test_image_size = 28 # 이미지 크기는 28x28 픽셀
# 이미지 저장 폴더 경로
test_set_folder = 'data/MNIST/test_set'
# 라벨 저장 파일 경로
test_label_file = 'data/MNIST/test_set/labels.txt'
# 압축 해제할 파일 경로 (test_set)
test_image_compressed_file = 'data/MNIST/raw/t10k-images-idx3-ubyte.gz'
test_label_compressed_file = 'data/MNIST/raw/t10k-labels-idx1-ubyte.gz'
# 압축 해제된 파일 경로 (test_set)
test_image_decompressed_file = 'data/MNIST/raw/t10k-images-idx3-ubyte'
test_label_decompressed_file = 'data/MNIST/raw/t10k-labels-idx1-ubyte'
# 이미지 파일 압축 해제 (test_set)
decompress_file(test_image_compressed_file, test_image_decompressed_file)
# 라벨 파일 압축 해제 (test_set)
decompress_file(test_label_compressed_file, test_label_decompressed_file)
# 이미지 저장 폴더 생성 (test_set)
if not os.path.exists(test_set_folder):
os.makedirs(test_set_folder)
# 라벨 데이터를 numpy 배열로 변환 (test_set)
with open(test_label_decompressed_file, 'rb') as f:
# 헤더 부분은 건너뛰기
f.read(8)
# 라벨 데이터 읽기
buf = f.read()
# 라벨 배열 형태로 변환 (test_set)
test_labels = np.frombuffer(buf, dtype=np.uint8)
# 라벨 저장 파일 열기 (test_set)
with open(test_label_file, 'w') as f:
# 이미지에 대한 라벨 값을 순차적으로 저장
for i in range(test_num_images):
image_path = f'{test_set_folder}/{test_labels[i]}/image_{i}.png' # 이미지 파일 경로 (image_{i:05}.png)
label_value = test_labels[i] # 라벨 값
# 이미지 파일 경로와 라벨 값을 파일에 저장
f.write(f'{image_path}\t{label_value}\n')
# 이미지 데이터를 numpy 배열로 변환 (test_set)
with open(test_image_decompressed_file, 'rb') as f:
# 헤더 부분은 건너뛰기
f.read(16)
# 이미지 데이터 읽기
buf = f.read()
# 이미지 배열 형태로 변환
test_images = np.frombuffer(buf, dtype=np.uint8).reshape(test_num_images, image_size, image_size)
# 이미지를 순서대로 저장 (test_set)
for i, image in enumerate(test_images):
label_value = test_labels[i] # 라벨 값
# 해당 숫자의 폴더 생성
digit_folder = os.path.join(test_set_folder, str(label_value))
if not os.path.exists(digit_folder):
os.makedirs(digit_folder)
# 이미지 파일 경로
image_path = os.path.join(digit_folder, f'image_{i}.png')
plt.imsave(image_path, image, cmap='gray')
두식을이용해, u와 v에대해풀어낸다. u = ((x4-x3)*(y1-y3)-(y4-y3)*(x1-x3)) / ((x2-x1)*(y4-y3)-(x4-x3)*(y2-y1)) v = ((x2-x1)*(y1-y3)-(y2-y1)*(x1-x3)) / ((x2-x1)*(y4-y3)-(x4-x3)*(y2-y1))
두선의각각의좌표를이용해 u와 v의값을구할수있음. 이때 u나 v가 0이면수평, 0 < u < 1이고 0 < v < 1이면교점이있는것임. u나 v가 1보다크면, 만나지않는것임. 구한 u와 v를직선의방정식에대입하면, 교점의좌표도구할수있음.
(2) 선과사각형
- 사각형의네선분에대해선과선충돌을검사.
float inputRotation = MathHelper.ToRadians(degree);
Rectangle destRect = new Rectangle(200, 200, 200, 200);
Vector2 vOrg = new Vector2(1, 0);
vOrg = Vector2.Transform(vOrg, Matrix.CreateRotationZ(inputRotation));
BoundingBox box = new BoundingBox(new Vector3(destRect.Left, destRect.Top, 0.0f),
new Vector3(destRect.Right, destRect.Bottom, 0.0f));
Ray ray = new Ray(new Vector3(vStart, 0.0f), new Vector3(vOrg, 0.0f));
float? result = box.Intersects(ray);
if (result != null && result <= lineLength)
{
// 충돌
}