자격증/AICE

AICE ex3

vy00 2024. 7. 12. 18:48

B 사 선박 입찰에 참여하려고 하고 있습니다. 코로나 19 로 위축되었던 물동량이 다시 살아나면서
선주들의 신규 선박에 대한 필요성이 늘어남에 따라 신규 선박 입찰 기회가 많아지고 있는 요즘입니다.
선박수주 가능성을 예측할 수 있으면, 해당 선박 입찰을 따내도록 더욱 집중적으로 노력할 수 있어 AI 기술을
활용하여 수주가능 여부를 예측하고자 합니다.
본 인증평가는 선박 수주관련 대내외 데이터를 분석하고 AI 모델링을 통해 선박의 수주 여부를 예측하는
문제로 구성되어 있습니다.

[데이터 컬럼 설명 (데이터 파일명: A0003MF.csv) ]
⚫ 유사선박수주경험: yes/no 유형으로 구분
⚫ 국제유가: 선박 수주 당시의 국제 유가
⚫ 환율: 선박 수주 당시의 환율
⚫ 선주사: 선주사 종류
⚫ 선종: 선박의 종류
⚫ 해운운임: 선박 수주 당시의 해운운임
⚫ 중국입찰여부 : yes/no 유형으로 구분
⚫ 선박크기 : 선박의 크기
⚫ 국내경쟁사입찰여부: yes/no 유형으로 구분
⚫ 수주잔고: 수주 당시의 금액 잔고
⚫ 입찰가: 선박 수주 당시의 입찰가
⚫ 수주여부: yes/no 유형으로 구분


1. Pandas 는 데이터 분석에 널리 사용되는 파이썬 라이브러리입니다.
Pandas 를 사용할 수 있도록 별칭(alias) pd 로 해서 불러오세요.
import pandas as pd


2. Matplotlib 은 데이터를 다양한 방법으로 시각화할 수 있도록 하는 파이썬 라이브러리입니다.
Matplotlib 의 pyplot 을 사용하기 위해 별칭(alias) plt 로 임포트하는 코드를 작성하고 실행하세요.
from matplotlib import pyplot as plt


다음 문항을 풀기 전에 아래 코드를 실행하세요.
!pip install seaborn
import tensorflow as tf
import numpy as np
import seaborn as sns
from tensorflow.python import keras
from tensorflow.python.keras.models import Sequential
from tensorflow.python.keras.layers import Dense, Dropout, BatchNormalization
from sklearn.model_selection import train_test_split
from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint
from tensorflow.keras.models import Model, load_model


3. AI 모델링을 위해 분석 및 처리할 데이터 파일을 읽어오려고 합니다.
Pandas 함수로 데이터 파일을 읽어 dataframe 변수에 할당하세요.
dataframe 변수명 : bids
데이터 파일명 : A0003MF.csv (.csv 파일은 본 문제/답안지와 동일한 경로에 있습니다)
Import pandas as pd
bids = pd.read_csv('./A0003MF.csv')  #bids=pd.read_csv('A0003MF.csv') 이것이 정답같다.

bids


4. 예측하려는 결과값의 분포를 확인하려고 합니다.
matplotlib 을 활용하여 '수주여부' 컬럼의 분포를 확인하는 histogram 그래프를 만드세요.
plt.hist
plt.hist(bids ['수주여부'])


4-1. 데이터컬럼의 분포를 확인하는 pairplot 그래프를 만드세요
Seaborn 을 활용하세요.
분포를 확인할 컬럼: 국제유가, 환율, 선박크기, 수주잔고
x 축에는 각각 컬럼이름으로 표시하고, Y 축에는 입찰가로 표시하세요

 

!pip install seaborn

import seaborn as sns

 

sns.pairplot(df, x_vars=['국제유가', '환율', '선박크기', '수주잔고'], y_vars='입찰가', kind='reg')

#kind='reg'는 회귀선을 포함한 산점도를 그리도록 지정

 

5. 환율과 국제유가에 따른 수주여부 상관관계를 확인해보고자 합니다.
'환율'과 '국제유가' 데이터 컬럼가지고 Seaborn 을 활용해서 lmplot 그래프를 만드세요.
X 축 : '환율'
Y 축 : '국제유가'
색깔구분 기준(hue) : '수주여부'
그래프 높이(height) :12
해당 안내 된 내용 외 별도의 파라미터를 입력하지 마시기 바랍니다.

여기에 답안코드를 작성하세요.

#bids는 데이터프레임 이름이다. 대부분 df를 사용할 것이다.
sns.lmplot(x='환율', y='국제유가', data=bids, hue='수주여부', height=12)


5-1. 위 그래프에서 확인가능한 아웃라이어 데이터를 삭제하려고 합니다.
선박크기, 수주잔고 컬럼의 아웃라이어 데이터를 삭제하세요
선박크기 350000 미만 대상을 삭제하세요
수주잔고 > 250 인 대상을 삭제하세요
#del df[“선박크기”>350000]
#del df[“수주잔고”>250]

 

#df는 데이터프레임 변수이름이다.

# 조건에 따라 아웃라이어 데이터 선택

condition = (df['선박크기'] >= 350000) | (df['수주잔고'] > 250)

# 조건을 만족하는 행을 삭제

df = df.drop(df[condition].index)

# 결과 확인

print(df)


6. AI 모델링 성능을 높이기 위해서는 데이터 결측치를 처리해야합니다.
아래 가이드에 따라 결측치를 처리하세요.
각 컬럼의 결측치를 확인하는 코드를 작성하고 실행하세요.
float 타입의 결측치 데이터는 0 으로 변환
object 타입의 결측치 데이터는 공백(' ')으로 변환

 

bids.dtypes
bids['국제유가'].replace(np.nan,'0',inplace=True)
bids['환율'].replace(np.nan,'0',inplace=True)
bids['해운운임'].replace(np.nan,'0',inplace=True)
bids['선박크기'].replace(np.nan,'0',inplace=True)
bids['수주잔고'].replace(np.nan,'0',inplace=True)
bids['입찰가'].replace(np.nan,'0',inplace=True)
bids['유사선박수주경험'].replace(np.nan,' ',inplace=True)
bids['선주사'].replace(np.nan,'',inplace=True)
bids['선종'].replace(np.nan,'',inplace=True)
bids['중국입찰여부'].replace(np.nan,'',inplace=True)
bids['국내경쟁사입찰여부'].replace(np.nan,'',inplace=True)
bids['수주여부'].replace(np.nan,'',inplace=True)

 

6-1.
수치형 데이터간에 상관성을 보려고 합니다.
상관계수를 구하여 heatmap 그래프로 시각화하세요
corr()함수로 상관계수를 구하여 heatmap 그래프로 시각화 하세요
annotation 를 포함하세요


import seaborn as sns import pandas as pd import matplotlib.pyplot as plt

# 예시 데이터프레임 생성 (실제 데이터프레임으로 대체하세요)

data = { '환율': [1100, 1120, 1150, 1180, 1200], '국제유가': [60, 65, 70, 75, 80], '선박크기': [300000, 360000, 320000, 370000, 330000], '수주잔고': [200, 260, 240, 270, 230], '입찰가': [50, 55, 60, 65, 70] }

 

df_total = pd.DataFrame(data)

# 상관계수 계산 및 히트맵 시각화

sns.heatmap(df_total.corr(), annot=True, cmap="RdBu") plt.show()

 


7. 범주형 데이터를 수치형 데이터로 변환해주는 데이터 전처리 방법 중 하나로 label encoder 를
사용합니다.
Scikit-learn 의 label encoder 를 사용하여 '선주사', ‘선종’ 데이터를 수치형 데이터로 변환하세요.
대상 컬럼 : ' 선주사', '선종'
해당 column 데이터를 변환하여 bids 에 반영


from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
bids['선주사'] = le.fit_transform(bids['선주사'])
bids['선종'] = le.fit_transform(bids['선종'])

 

(7-1) 범주형 데이터를 수치형 데이터로 변환해 주는 데이터 전처리 방법 중 하나로 label
encoder 를 사용합니다.
Scikit-learn 의 label encoder 를 사용하여 범주형 데이터를 수치형 데이터로 변환하세요
전처리대상컬럼: 유사선박수주경험, 선주사, 선종, 중국입찰여부, 국내경쟁사 입찰여부
fit_transform 을 활용하세요. 수주여부 컬럼은 삭제하세요
#del bids[‘수주여부’]

 

import pandas as pd
from sklearn.preprocessing import LabelEncoder

# 예시 데이터프레임 생성 (실제 데이터프레임으로 대체하세요)
data = {
    '유사선박수주경험': ['있음', '없음', '있음', '없음', '있음'],
    '선주사': ['A사', 'B사', 'C사', 'A사', 'C사'],
    '선종': ['탱커', '벌크', '컨테이너', '탱커', '컨테이너'],
    '중국입찰여부': ['예', '아니오', '예', '예', '아니오'],
    '국내경쟁사 입찰여부': ['예', '예', '아니오', '예', '아니오'],
    '수주여부': ['성공', '실패', '성공', '실패', '성공']
}
bids = pd.DataFrame(data)

# 수주여부 컬럼 삭제
bids = bids.drop(columns=['수주여부'])

# LabelEncoder를 사용하여 범주형 데이터를 수치형 데이터로 변환
label_encoders = {}
for column in ['유사선박수주경험', '선주사', '선종', '중국입찰여부', '국내경쟁사 입찰여부']:
    le = LabelEncoder()
    bids[column] = le.fit_transform(bids[column])
    label_encoders[column] = le

# 결과 확인
print(bids)


8. 원-핫 인코딩은 범주형 변수를 1 과 0 의 이진형 벡터로 변환하기 위하여 사용하는 방법입니다.
원-핫 인코딩(One-hot encoding)으로 아래 조건에 해당하는 컬럼 데이터를 변환하세요.
⚫ 원-핫 인코딩 대상: object 타입의 컬럼
⚫ drop_first 옵션을 통해 가변수 1 개 삭제
⚫ 해당 column 데이터를 변환하여 bids 에 반영
bids = pd.get_dummies(data=bids, columns =
['유사선박수주경험','선주사','선종','중국입찰여부','국내경쟁사입찰여부','수주여부'], drop_first=True)
(8-1)
원-핫 인코딩은 범주형 변수를 1 과 0 으로 이진행 벡터로 변환하기 위하여 사용하는 방법입니다.
원-핫 인코딩으로 아래 조건에 해당하는 컬럼 데이터를 변환하세요
• 원-핫인코딩 대상: object 타임 컬럼
Pandas 의 get_dummies 함수를 활용하고 drop_first 는 True 파라미터를 추가하세요

 

import pandas as pd

# 예시 데이터프레임 생성 (실제 데이터프레임으로 대체하세요)
data = {
    '유사선박수주경험': ['있음', '없음', '있음', '없음', '있음'],
    '선주사': ['A사', 'B사', 'C사', 'A사', 'C사'],
    '선종': ['탱커', '벌크', '컨테이너', '탱커', '컨테이너'],
    '중국입찰여부': ['예', '아니오', '예', '예', '아니오'],
    '국내경쟁사 입찰여부': ['예', '예', '아니오', '예', '아니오']
}
bids = pd.DataFrame(data)

# 원-핫 인코딩 대상: object 타입의 컬럼
object_columns = bids.select_dtypes(include=['object']).columns

# 원-핫 인코딩 수행
bids = pd.get_dummies(data=bids, columns=object_columns, drop_first=True)

# 결과 확인
print(bids)


9. '수주여부_yes'컬럼을 Label(y)로, 나머지 컬럼을 Feature(X)로 할당한 후 훈련데이터셋과
검증데이터셋으로 분리하세요.
⚫ 대상 데이터 : bids
⚫ 훈련 데이터셋 label : y_train, 훈련 데이터셋 Feature: X_train
⚫ 검증 데이터셋 label : y_valid, 검증 데이터셋 Feature: X_valid
⚫ 훈련 데이터셋 과 검증데이터셋 비율은 70: 30
⚫ random_state : 42
Scikit-learn 의 train_test_split 함를 활용하세요.
데이터 분리시 y 데이터를 원래 데이터의 분포와 유사하게 추출되도록 옵션을 적용하세요.

 

from sklearn.model_selection import train_test_split
X = bids.drop(['수주여부_yes'],axis=1)
y = bids['수주여부_yes']

X_train, X_valid, y_train, y_valid = train_test_split(X, y, stratify=y, test_size=0.2, random_state=42)

 

(9-1) 훈련과 검증 각각에 사용할 데이터셋을 분리하려고 합니다.
수주여부 컬럼을 Label(y), 나머지 컬럼을 Feature(x)로 할당한 후 훈련데이터셋과
검증데이터셋으로 분리하세요
Scikit-learn 의 train_test_split 함수를 활용하세요
x, y 데이터러부터 훈련데이터셋과 검증데이터셋을 80:20 비율로 분리하세요
random_state 는 58 로 설정하세요

import pandas as pd
from sklearn.model_selection import train_test_split

# 예시 데이터프레임 생성 (실제 데이터프레임으로 대체하세요)
data = {
    '유사선박수주경험': ['있음', '없음', '있음', '없음', '있음'],
    '선주사': ['A사', 'B사', 'C사', 'A사', 'C사'],
    '선종': ['탱커', '벌크', '컨테이너', '탱커', '컨테이너'],
    '중국입찰여부': ['예', '아니오', '예', '예', '아니오'],
    '국내경쟁사 입찰여부': ['예', '예', '아니오', '예', '아니오'],
    '수주여부': ['성공', '실패', '성공', '실패', '성공']
}
bids = pd.DataFrame(data)

# 수주여부 컬럼을 Label(y), 나머지 컬럼을 Feature(x)로 할당
x = bids.drop('수주여부', axis=1)
y = bids['수주여부']

# 훈련 데이터셋과 검증 데이터셋으로 분리 (80:20 비율, random_state=58)
x_train, x_val, y_train, y_val = train_test_split(x, y, test_size=0.2, random_state=58)

# 결과 확인
print("x_train:\n", x_train)
print("x_val:\n", x_val)
print("y_train:\n", y_train)
print("y_val:\n", y_val)


10. 데이터들이 동일한 중요도로 반영되도록 하기 위해 데이터 정규화를 하려고 합니다.
StandardScaler 를 사용하여 아래 조건에 따라 데이터 변수를 정규분포화, 표준화 하세요.
Scikit-learn 의 StandardScaler 를 사용하세요.
train set 은 정규분포화(fit_transform)를 하세요.
valid set 은 표준화(transform)를 하세요.
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_valid = scaler.transform(X_valid)


11. 랜덤 포레스트(Random Forest)는 분류, 회귀 분석 등에 사용되는 앙상블 학습 방법입니다.
아래 하이퍼 파라미터 설정값을 적용하여 RandomForestClassifier 모델로 학습을 진행하세요.
결정트리의 개수는 100 개로 설정하세요.
최대 feature 개수는 9 로 설정하세요.
트리의 최대 깊이는 15 로 설정하세요.
random_state 는 42 로 설정하세요
score 함수를 사용하여 성능을 출력하세요


from sklearn.ensemble import RandomForestClassifier
RF = RandomForestClassifier(n_estimators=100, max_depth = 15, max_features=9, random_state=42)
RF.fit(X_train, y_train)
score = RF.score(X_train, y_train)

 

(11-1) 랜덤포레스트(Random Forest)는 분류, 회귀분석에 사용되는 앙상블 학습방법입니다.
아래 하이퍼파라미터 설정값을 적용하여 RandomForestRegressor 모델로 학습을 진행하세요.
설정트리의 개수는 70 개로 설정하세요
트리의 최대 깊이는 12 로 설정하세요
random_state 는 42 로 설정하세요


from sklearn.ensemble import RandomForestRegressor
rf = RandomForestRegressor(n_estimators=70, max_depth=12, random_state=42)
rf.fit(x_train, y_train)


12. 위 모델의 성능을 평가하려고 합니다.
y 값을 예측하여 confusion matrix 를 구하고 heatmap 그래프로 시각화하세요.
또한, Scikit-learn 의 classification report 기능을 사용하여 성능을 출력하세요.
11 번 문제에서 만든 모델로 y 값을 예측(predict)하여 y_pred 에 저장하세요.
Confusion_matrix 를 구하고 heatmap 그래프로 시각화하세요. 이때 annotation 을 포함시키세요.
Scikit-learn 의 classification report 기능을 사용하여 클래스별 precision, recall, f1-score 를
출력하세요.

 

from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import confusion_matrix, classification_report
import seaborn as sns
import matplotlib.pyplot as plt

# RandomForestClassifier 모델 생성 및 학습
RF = RandomForestClassifier(n_estimators=100, max_depth=15, max_features=9, random_state=42)
RF.fit(X_train, y_train)

# 모델의 성능 출력
train_score = RF.score(X_train, y_train)
print(f"Training Score: {train_score}")

# 검증 데이터셋에 대한 예측 수행
y_pred = RF.predict(X_valid)

# Confusion Matrix 계산
conf_mat = confusion_matrix(y_true=y_valid, y_pred=y_pred)

# Confusion Matrix 히트맵 시각화
sns.heatmap(conf_mat, annot=True, cmap="Blues", fmt='g')
plt.title('Confusion Matrix')
plt.xlabel('Predicted')
plt.ylabel('Actual')
plt.show()

# Classification Report 출력
print("Classification Report:\n", classification_report(y_valid, y_pred))

 

# Classification report 출력

#print("Classification Report: \n", classification_report(y_val, y_pred_binary)) 이 방법도 있다.

 

Classification Report:
precision recall f1-score support
0 0.90 0.98 0.93 299
1 0.93 0.74 0.82 129
accuracy 0.90 428
macro avg 0.91 0.86 0.88 428
weighted avg 0.91 0.90 0.90 428

 

(12-1) 위 모델의 성능을 평가하려고 합니다. 예측결과의 mae와 mse를 출력하세요
위 모델의 예측값을 y_pred 에 저장하세요
y_pred 의 mae 와 mse 를 각각 순서대로 출력하세요

 

from sklearn.metrics import mean_absolute_error, mean_squared_error


y_pred = rf.predict(X_test)
mae = mean_absolute_error(y_test, y_pred)
print('MAE:', mae)
mse = mean_squared_error(y_test, y_pred)
print('MSE:', mse)


13. 선박의 수주여부를 예측하는 딥러닝 모델을 만들려고 합니다.
아래 가이드에 따라 모델링하고 학습을 진행하세요
Tensorflow framework 를 사용하여 딥러닝 모델을 만드세요.
히든레이어(hidden layer) 3 개이상으로 모델을 구성하고 과적합 방지하는 dropout 을 설정하세요.
EarlyStopping 콜백으로 정해진 epoch 동안 모니터링 지표가 향상되지 않을 때 훈련을 중지하도록
설정하세요.(모니터링 지표 : val_loss).
ModelCheckpoint 콜백으로 validation performance 가 좋은 모델을 'best_model.h5' 파일로
저장하세요.


import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Activation, Dropout
from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint

model = Sequential()
model.add(Dense(128, activation='relu', input_shape=(31,)))
model.add(Dropout(0.5))
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(64, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(32, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(1, activation='sigmoid'))

 

#모델 컴파일
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['acc'])

 

#콜백설정
checkpoint_path = "best_model.h5"
checkpoint = ModelCheckpoint(filepath=checkpoint_path,
save_weights_only=True,
save_best_only=True,
monitor='val_acc',
verbose=1)
es = EarlyStopping(monitor='val_loss', patience=4, mode='min', verbose=1)

 

#모델학습
history = model.fit(X_train, y_train, epochs=10,
validation_data = (X_valid, y_valid),
callbacks=[checkpoint, es])

 

Epoch 1/10
51/54 [===========================>..] - ETA: 0s - loss: 0.7424 - acc: 0.5925
Epoch 00001: val_acc improved from -inf to 0.69860, saving model to best_model.h5
54/54 [==============================] - 1s 25ms/step - loss: 0.7424 - acc: 0.5927 -
val_loss: 0.5909 - val_acc: 0.6986
Epoch 2/10
50/54 [==========================>...] - ETA: 0s - loss: 0.5477 - acc: 0.7387
Epoch 00004: val_acc improved from 0.77336 to 0.82009, saving model to best_model.h5
Epoch 00009: val_acc improved from 0.85514 to 0.86449, saving model to best_model.h5
54/54 [==============================] - 1s 19ms/step - loss: 0.4736 - acc: 0.7946 -
val_loss: 0.3699 - val_acc: 0.8645
Epoch 10/10
53/54 [============================>.] - ETA: 0s - loss: 0.4568 - acc: 0.8084
Epoch 00010: val_acc did not improve from 0.86449
54/54 [==============================] - 1s 21ms/step - loss: 0.4604 - acc: 0.8075 -
val_loss: 0.3599 - val_acc: 0.8598

 

(13-1) 선박수주금액을 예측하는 딥러닝 모델을 만들려고 합니다. 아래 가이드에 따라 모델링하고
학습을 진행하세요
Tensorflow framework 를 사용하여 딥러닝 모델로 만드세요
히든레이어(hidden layer)2 개이상으로 모델을 구성하고 과적합 방지하는 dropout 을 설정하세요
EarlyStopping 콜백으로 정해진 epoch 동안 모니터링 지표가 향상되지 않을 때 훈련을 중지하도록
설정하세요
ModelCheckpoint 콜백으로 validation performance 가 좋은 모델을 best_model.h5 파일로
저장하세요


#답안추가필요
model = Sequential()
model.add(Dense(64, activation='relu', input_shape=( X_train.shape[1],,))) # 입력 레이어 추가
model.add(Dropout(0.2)) # 드롭아웃을 이용한 과적합 방지
model.add(Dense(64, activation='relu')) # 은닉층 추가
model.add(Dropout(0.2)) # 드롭아웃을 이용한 과적합 방지
model.add(Dense(1, activation='linear')) # 출력 레이어 추가

 

#모델 컴파일
model.compile(loss='mse', optimizer='adam', metrics=['mae'])

 

#콜백 설정
early_stopping = EarlyStopping(monitor='val_loss', patience=5)
model_checkpoint = ModelCheckpoint('best_model.h5', monitor='val_acc', save_best_only=True,
mode='max')

 

#모델 학습
history = model.fit(X_train, y_train, epochs=100, batch_size=32, validation_data=(X_val, y_val),
callbacks=[early_stopping, model_checkpoint])
model.load_weights('best_model.h5')


14. 위 딥러닝 모델의 성능을 평가하려고 합니다.
학습정확도/손실, 검증정확도/손실을 그래프로 표시하세요.
1 개의 그래프에 학습정확도 및 손실, 검증정확도 및 손실 4 가지를 모두 표시하세요.
위 4 가지 각각의 범례를 'acc', 'loss', 'val_acc', 'val_loss'로 표시하세요.
그래프의 타이틀은 'Accuracy'로 표시하세요.
X 축에는 'Epochs'라고 표시하고 Y 축에는 'Acc'라고 표시하세요.
plt.plot(history.history['acc'])
plt.plot(history.history['loss'])
plt.plot(history.history['val_acc'])
plt.plot(history.history['val_loss'])
plt.title('Accuracy')
plt.xlabel('Epochs')
plt.ylabel('Acc')
plt.legend(['acc','loss', 'val_acc','val_loss'])
plt.show()

 

(14-1)위 딥러닝 모델의 성능을 평가하려고 합니다. mae 를 그래프로 표시하세요
그래프에 mae, val_mae 를 표시하세요
위 2 가지 각각의 범례를 mae, val_mae 로 표시하세요
x 축에는 Epochs 라고 표시하고 Y 축에는 MAE 라고 표시하세요

 

import matplotlib.pyplot as plt


mae = history.history['mae']
val_mae = history.history['val_mae']
plt.plot(range(1, len(mae) + 1), mae, label='mae')
plt.plot(range(1, len(val_mae) + 1), val_mae, label='val_mae')
plt.xlabel('Epochs')
plt.ylabel('MAE')
plt.legend(['mae', 'val_mae'])
plt.title('MAE and val_MAE')
plt.show()

'자격증 > AICE' 카테고리의 다른 글

AICE ex2  (0) 2024.07.12
AICE ex  (3) 2024.07.11
AICE Associate 정리  (0) 2024.07.10