본문 바로가기

Data Science : Project/개인 프로젝트

캐글 샌프란시스코 범죄발생률 예측 : train & test

반응형

 

 

▶이전글 : EDA

 

캐글 샌프란시스코 범죄발생률 예측 : EDA

파이썬으로 진행하였고, 데이터 분석 강의 4주차를 마무리하고 캐글에 최종적으로 제출한 버전을 기반으로 코드를 설명했다. (중간 과정이 궁금하다면 github를 참고! github.com/helloMinji/Kaggle_crime_Sa

hellominji.tistory.com

 

 

 


데이터 전처리

여기서는 train에 대해서만 기재하지만, test에도 같은 과정을 거쳤다.

 

 1  Dates

train["Dates-year"] = train["Dates"].dt.year
train["Dates-month"] = train["Dates"].dt.month
train["Dates-day"] = train["Dates"].dt.day
train["Dates-hour"] = train["Dates"].dt.hour
train["Dates-minute"] = train["Dates"].dt.minute
train["Dates-second"] = train["Dates"].dt.second

# 분에서 30을 뺀 후 절댓값
train["Dates-minute(abs)"] = np.abs(train["Dates-minute"]-30)

시간을 단위별로 나눈다.(연, 월, 일, 시, 분, 초)

분은 자세히 기록되어 있지 않기 때문에, 분에 30을 뺀 후 절대값을 씌운다. 이렇게 하면 0분을 기준으로 상대적인 플러스/마이너스치를 알 수 있다.

 

 

 2  DayOfWeek

train_dayofweek = pd.get_dummies(train["DayOfWeek"], prefix = "DayOfWeek")
train = pd.concat([train, train_dayofweek], axis = 1)

요일은 문자라서 머신이 인식할 수 없다. 이를 위해 one-hot encoding을 진행한다.

 

 

 3  PdDistrict

train_pddistrict = pd.get_dummies(train["PdDistrict"], prefix = "PdDistrict")
train = pd.concat([train, train_pddistrict], axis = 1)

경찰서의 이름은 문자라서 머신이 인식할 수 없다. 이를 위해 one-hot encoding을 진행한다.

 

 

 4  Address

train["Crossroad"] = train["Address"].str.contains("/")

교차점인지 아닌지에 따라 특정 범죄의 발생률에 차이가 있기 때문에 변수로 활용하기 위하여 새로운 컬럼을 생성하였다.

 

def clean_address(address):
    if "/" not in address:
        return address

    address1, address2 = address.split("/")
    address1, address2 = address1.strip(), address2.strip()

    if address1<address2:
        address = "{} / {}".format(address1, address2)
    else:
        address = "{} / {}".format(address2, address1)

    return address

train["Address(clean)"] = train["Address"].apply(clean_address)

교차로 이름이 a/b와 b/a로 순서가 다른 경우가 있기 때문에

주소값을 비교하여 알파벳이 빠를수록 작은 값이라고 가정하고, 작은 값을 앞으로, 큰 값을 뒤로 배치하여 교차로의 이름을 통일시킨다.

 

address_counts = train["Address(clean)"].value_counts()

top_address_counts = address_counts[address_counts >= 100]
top_address_counts = top_address_counts.index

train.loc[~train["Address(clean)"].isin(top_address_counts), "Address(clean)"] = "Others"

주소값을 많은 순으로 정렬하고, 주소 발생 횟수가 100회 미만인 주소들은 Others로 값을 통일하여 다른 값에 대한 정확도를 높인다.

 

train_address = pd.get_dummies(train["Address(clean)"])

from scipy.sparse import csr_matrix
train_address = csr_matrix(train_address)

주소는 데이터 특성 상 one-hot encoding 후에는 해당 열에 0의 비율이 굉장히 높게 만들어진다.

이에 메모리의 효율적인 사용을 위해 CSR Matrix로 변환한다.

 

▶CSR Matrix란?

 

Python : csr_matrix (데이터의 수를 줄이는 방법)

5 7 1 4 2 3 6 2 1 6 4 5 7 희소행렬: 대부분의 값이 0. 이런 희소행렬을 메모리 낭비가 적도록 변환하고자 한다! → CSR 형식을 사용! ▶ 필요한 패키지 불러오기 from scipy.sparse import csr_matrix import n..

hellominji.tistory.com

 

 

 


학습

feature_names = ["X", "Y", "Dates-year", "Dates-month", "Dates-day", "Dates-hour", "Dates-minute(abs)", "Dates-second"]
feature_names = feature_names + list(train_dayofweek.columns)
feature_names = feature_names + list(train_pddistrict.columns)
label_name = "Category"

X_train = train[feature_names]
X_test = test[feature_names]
y_train = train[label_name]

# CSR Matrix 합치기 - hstack
from scipy.sparse import hstack

X_train = hstack([X_train.astype('float'), train_address])
X_train = csr_matrix(X_train)
X_test = hstack([X_test.astype('float'), test_address])
X_test = csr_matrix(X_test)

전처리를 통해 생성된 컬럼들을 feature_names로 만든다.

hstack을 이용하여 원래 train 데이터와 CSR Matrix를 합치고, 이를 다시 CSR Matrix로 변환한다.

 

 

 


모델 생성

하이퍼 파라미터 최적화를 통해 파라미터값을 정한다

coarse to fine

 

하이퍼파라미터 튜닝 : Coarse & Fine Search

모델의 가장 성능의 좋은 파라미터를 선택하기 위해, 하이퍼파라미터 튜닝 작업을 거친다. 이 때 Coarse & Fine Search를 사용하여 파라미터를 찾는다. Coarse Search Random Search를 하되, 이론상으로 존재

hellominji.tistory.com

 

 

이를 통해 생성된 최종 모델은 다음과 같다.

best_hyperparameters = finer_hyperparameters_list.iloc[0]

Lgb = LGBMClassifier(n_estimators = best_hyperparameters['n_estimators'],
                       learning_rate = best_hyperparameters['learning_rate'],
                       num_leaves = best_hyperparameters['num_leaves'],
                       max_bin = best_hyperparameters['max_bin'],
                       min_child_samples = best_hyperparameters['min_child_samples'],
                       subsample = best_hyperparameters['subsample'],
                       subsample_freq = best_hyperparameters['subsample_freq'],
                       colsample_bytree = best_hyperparameters['colsample_bytree'],
                       class_type = best_hyperparameters['class_type'],
                       random_state = best_hyperparameters['random_state'])

 

 

 


평가 및 예측

from sklearn.model_selection import train_test_split    # 데이터를 일정 비율로 두 개로 쪼개줌

# train 데이터를 train_kf, test_kf로 쪼개서 모델성능 평가
X_train_kf, X_test_kf, y_train_kf, y_test_kf = train_test_split(X_train, y_train, test_size = 0.3, random_state = 37)

# 학습(LightGBM)
%time Lgb.fit(X_train_kf, y_train_kf)

# 범죄가 발생할 확률(LightGBM)
y_predict_test_kf = Lgb.predict_proba(X_test_kf)

# score 계산: 캐글에 업로드하지 않아도 순위 예상 가능
from sklearn.metrics import log_loss

score = log_loss(y_test_kf, y_predict_test_kf)
print(f"Score = {score:.5f}")



### Predict
%time Lgb.fit(X_train, y_train)
prediction_list = Lgb.predict_proba(X_test)   # 진짜 test로 예측

 

해당 컴페티션의 score 방식을 확인하여 결과파일을 업로드하지 않아도 대략적인 score를 예측할 수 있도록 한다.

 

※ 예측에 쓰이는 함수가 predict_proba?

컴페티션에서 요구하는 예측 결과가 하나만 예측하라!가 아닌 각각의 범죄가 일어날 확률로 되어있기 때문!

 

 

 


제출

sample_submission = pd.read_csv("sampleSubmission.csv", index_col = "Id")

submission = pd.DataFrame(prediction_list,
                          index = sample_submission.index,
                          columns = model.classes_)

submission.to_csv("baseline-script.csv")

 

 

나의 스코어는 2.30406으로 약 상위 15.2% 정도였다.

강의를 들으면서 좋았던 건, LightGBM과 coarse to fine 방법을 배운 것이다. 이 두가지는 예측 score를 상당히 높이는데 도움이 되었다.

 

 

 

 

 

반응형