선발대

[스파르타] 실전 머신러닝 적용 1주차 (완강) 본문

스파르타코딩클럽/강의 정리

[스파르타] 실전 머신러닝 적용 1주차 (완강)

신선한 스타트 2022. 1. 5. 17:24

1. 수업 후기

 

  • 강의 개수: 10개
  • 총 강의시간: 1시간 42분
  • 수업 목표:
  • 1. 머신러닝의 기초 개념을 알아본다.
  • 2. 선형 회귀에 대해 배운다.
  • 3. Colab과 Kaggle을 이용해 직접 실습해본다!

 

말로만 듣던 머신러닝을 처음 배우게 되었다. 기존에 가지고 있었던 사전 지식은 머신러닝은 수학을 잘해야 하고, 최소 석사 학위가 있어야 한다는 등의 내용이 다수였다. 머신러닝이라고 하면 왠지 인공지능과 관련된 AI이나 아이언맨의 자비스가 떠오른다. 과연 무슨 내용이길래 이렇게 진입장벽이 높은가 하고 궁금했는데 이번 강의를 듣고 머신러닝이라는 것에 대해 알 수 있어서 재밌었다.

 

머신러닝은 말 그대로 기계가 학습하는 것으로, 사람이 계산하기 힘든 것을 전부 기계에게 넘기는 것이다. 우리는 넘길 때 데이터만 잘 다듬어주면 된다. 명령어도 처음 봐서 생소했을 뿐, 실습할 때는 대부분 고정된 기능만 사용해서 직관적으로 이해할 수 있었다. 물론 세부적으로 진입해서 어떻게 작동하는지 이해하고, 더 효율적인 구조를 구성하려면 지금보다 훨씬 더 복잡한 난이도로 상승하겠지만 우선 첫 소감으로는 머신러닝이 흥미롭다. 그럼 다음 강의도 화이팅~

 

 

2. 수업내용 정리

1-1. 1주차 오늘 배울 것
더보기

01. 머신러닝

 

  • 기계를 학습시키는 것이 무슨 이야기일까?

 

02. 선형 회귀

 

  • 선형 회귀: 두 데이터 간의 직선관계를 찾아내어 x값이 주어졌을 때 y값을 예측하는 것.

 

03. 강의에 들어가기에 앞서

 

  • 1. 영어로 된 용어를 사용하길 권장하는 이유
  • 구글, Stackoverflow 등의 사이트에서 영어를 많이 씀
  • 의사소통시 영어로 소통해야 의사소통 오류 적음
  • 외국인 엔지니어와의 의사소통
  • 외국계 기업 취업

 

  • 2. (질문보다) 구글링의 중요성
  • 실무에서 실제로 구글링이 차지하는 비율이 90% 이상
  • 검색 능력이 곧 업무 성과에 비례하는 경우가 많음
  • 새로운 분야를 개척할수록 질문할 사람(사수)가 없는 경우가 많음

 

1-2. 필수 계정 가입하기
더보기

01. Gmail

02. Kaggle

 

1-3. 머신러닝이란?
더보기

01. 알고리즘이란?

 

  • 알고리즘: 수학과 컴퓨터과학, 언어학 또는 관련 분야에서 어떠한 문제를 해결하기 위해 정해진 일련의 절차나 방법을 공식화한 형태로 표현한 것, 계산을 실행하기 위한 단계적 절차
  • ex) '전날 마신 커피잔 수에 따른 시험 점수' 라는 알고리즘
  • 실제 상황에선 세부적인 요인들이 정말 많은데, 이걸 컴퓨터에게 해결하도록 하는 머신러닝 탄생.
  • 머신러닝(기계학습)이라는 포괄적인 범위 안에 딥러닝(Deep learning)이 포함됨.
  • 연구 초반에는 MLP(Multi-Layer Perceptron)이라고 불렸으나, 유행타면서 딥러닝으로 굳어짐.
  • 머신러닝 중 딥러닝은 고차원의 비선형 문제를 잘 풀 수 있어, 빠른 속도로 발전함.
  • 최근 10년간 모든 학문 분야 중 가장 많은 연구가 이루어졌고 덕분에 파이썬도 전성기를 맞게 됨.

 

02. 머신러닝의 회귀와 분류

 

  • 모든 문제를 풀이하려면 입력값, 출력값을 정의해야 함.
  • 회귀(Regression): 출력값이 연속적인 소수점으로 예측하게 하도록 푸는 방법
  • ex) 사람의 얼굴 사진을 보고 몇 살인지 예측하는 문제 (연속적인 문제)
  • 아래는 분류를 해서 문제를 풀이하는 방법임.
  • 이진분류(Classification): 이진클래스(Binary class)로 나누는 경우. ex) 과목의 이수여부 (비연속적)
  • 다중분류(Multi-class classification, Multi-label classification): 클래스 개수가 2개보다 많은 경우.
  • 회귀, 분류가 둘다 가능한 문제: 나이를 클래스(범위)로 나누어 생각할수도.
  • 인풋, 아웃풋 값을 정의하고 아웃풋값에 따라 문제를 접근하면 편함.

 

03. 지도 학습 / 비지도 학습 / 강화 학습

 

  • 머신러닝은 크게 3가지로 분류됨. 지도, 비지도, 강화학습

 

  • 지도학습(Supervised learning): 정답을 알려주면서 학습시키는 방법
  • 입력값, 출력값 전부 알려준다. 대신 정답(출력값)이 없으면 이 방법으로 학습 불가능함.
  • 입력값에 정답을 하나씩 입력해주는 노가다 작업: 라벨링(labeling), 어노테이션(Annotation)

 

  • 비지도학습(Unsupervised learning): 정답을 알려주지 않고 군집화(Clustering)하는 방법
  • 그룹핑 알고리즘(Grouping algorithm)의 성격을 띄고 있음.
  • 학습시간이 오래 걸리고 학습방식도 복잡함.
  • 정답(출력값)이 없을 때 사용. 라벨(Label, Class)가 없는 데이터로 문제 풀이할 때 큰 힘 발휘.
  • ex) 음원파일을 분석하여 장르를 팝, 클래식 락, 댄스 등으로 나누는 문제
  • 군집(Clustering): K-평균(K-Means), 계측군집분석(HCA), 기댓값 최대화(Expectation Maximization)
  • 시각화와 차원축소: 주성분 분석(PCA), 커널 PCA, 지역적 선형 임베딩(LLE), t-SNE
  • 연관 규칙 학습: 어프라이어리, 이클렛

 

  • 강화학습(Reinforcement learning): 주어진 데이터 없이 실행, 오류를 반복하면서 학습하는 방법
  • 알파고를 탄생시킨 머신러닝 방법. 행동 심리학에서 나온 이론.
  • 데이터가 있어도 정답이 따로 정해져 있지 않고, 자신의 행동에 대해 보상을 받으며 학습하는 것.
  • 강화학습의 개념: 에이전트, 환경, 상태, 행동, 보상
  • ex) 에이전트가 게임환경의 현재 상태에서 높은 점수를 얻는 방법을 찾아가며 행동하는 학습 방법으로 특정 학습 횟수를 초과하면 높은 점수를 획득할 수 있는 전략이 형성됨. (단, 행동을 위한 행동목록은 사전정의)
  • 강화학습은 이전부터 존재했던 학습법이지만, 최근 딥러닝 등장 이후 강화학습에 신경망 적용이 가능해지면서 바둑, 자율주행차 같은 복잡한 문제에 적용가능하게 됨.

 

1-4. 선형 회귀 (Linear Regression)
더보기

01. 선형 회귀와 가설, 손실 함수 Hypothesis & Cost function (Loss function)

 

  • 머신러닝 초기에는 과학자들이 모든 문제를 선형으로 풀 수 있다고 가정했기에, 선형 회귀 등장

 

  • 가설(Hypothesis): 우리가 임의로 만든 직선 H(x) = Wx + b 
  • 기계가 W(weight), b(bias)을 계속 변경하면서 손실함수를 줄인다.
  • 머신러닝은 선형 1차함수에서 시작하는 것.
  • 머신러닝에서 모든 문제는 가설 있어야 풀이할 수 있음. 가설에 맞춰서 문제 풀이하도록 학습 시킴.

 

  • 손실함수(Cost or Loss function): Cost (가설과 정답의 거리, Mean squared error)
  • 예시는 1차 함수였지만 실무 머신러닝 모델은 고차원 함수를 쓴다는 점만 빼고는 동일함.
  • 우리가 하는 일: 데이터 보고 비슷한 함수 모양의 가설을 세운 뒤, 그에 맞는 손실 함수를 정의함.
  • 기계가 하는 일: 손실함수 보고 우리의 가설에 맞출 수 있도록 열심히 계산함.
  • 그래서 기계학습(머신러닝)이라는 이름이 붙음.
  • 손실함수 종류: https://keras.io/ko/losses/
 

Losses - Keras Documentation

손실 함수의 사용 손실 함수(목적 함수 또는 최적화 점수 함수)는 하나의 모델을 컴파일하기 위해 필요한 두 개의 매개 변수 중 하나입니다. model.compile(loss='mean_squared_error', optimizer='sgd') from keras i

keras.io

 

02. 다중 선형 회귀 (Multi-variable linear regression)

 

  • 다중 선형 회귀: 입력값이 2개 이상이 되는 문제를 선형 회귀로 풀이하고 싶을 대 사용함 여러 개의 입력변수.
  • ex) 커피잔 수, 게임 플레이 시간과 시험점수간의 상관관계
  • 개념은 동일함. 직선을 하나 생각하고 정답값과의 거리를 계산하는 것임.
  • 가설 H(x1, x2, ... , xn) = w1x1 + w2x2 + w3x3 + ... + wnxn + b

 

1-5. 경사 하강법 (Gradient descent method)
더보기

01. 경사 하강법이란?

 

  • 우리의 목표: 손실함수를 최소화(Optimize)하는 것.

 

  • 경사하강법: 처음에 랜덤한 점에서 시작함. 한번씩 움직이면서 그래프 최소점에 도달하면 학습 종료. 
  • 기계가 그래프 따라 이동하면서 W, b를 변경하고 cost가 줄어들었는지 확인함.
  • Optimizer > Gradient descent 포함

 

02. Learning rate

 

  • Learning rate: 손실함수에서 한 칸씩 전진하는 단위
  • 머신러닝 모델이 학습을 잘하기 위해서는 적당한 learning rate를 찾는 것이 필수적임.
  • 너무 작으면, 초기값에서 최소점을 찾을 때 많은 시간이 걸림. 
  • 너무 크면, 최소값을 지나치고 계속 진동하다가 최악의 경우에는 발산하게 될 수도 있음. → Overshooting

 

03. 실제로 손실 함수를 그릴 수 있을까?

 

  • 우리의 목표: 손실함수의 최소점인 Global cost minimum을 찾는 것.
  • 한 칸씩 움직이는 스텝(Learning rate) 잘못 설정하면 Local cost minimum에 빠질 가능성이 높음.
  • Cost가 높다는 얘기는 우리가 만든 모델의 정확도가 낮다는 것을 의미함.
  • 머신러닝 엔지니어의 핵심역할: Global minimum을 찾기 위해 좋은 가설, 손실함수를 만들어야 함.

 

1-6. 데이터셋 분할
더보기

01. 학습 / 검증 / 테스트 데이터

 

출처: https://3months.tistory.com/118

 

  • 1. Training set(학습 데이터셋, 트레이닝셋) = 교과서
  • 머신러닝 모델 학습시키는 용도로 사용. 전체 데이터 셋의 약 80%정도 차지함.

 

  • 2. Validation set(검증 데이터셋, 밸리데이션셋) = 모의고사
  • 머신러닝 모델 성능을 검증하고 튜닝하는 지표의 용도로 사용함.
  • 정답라벨이 있고 학습 단계에서 사용하지만, 모델에게 데이터를 직접 보여주지 않아 모델성능에 영향 X.
  • 손실함수, Optimizer 등을 바꾸면서 모델을 검증하는 용도로 사용함.
  • 전체 데이터셋의 약 20% 정도를 차지함.

 

  • 3. Test set(평가 데이터셋, 테스트셋) = 수능
  • 정답 라벨이 없는 실제 환경에서의 평가 데이터셋. 학습에서 사용하지 않은 순수한 데이터셋.
  • 검증 데이터셋으로 평가된 모델이 정확도가 높아도 실제 사용자 제품에서 제대로 돌아가지 않으면 꽝.
  • 실제 실무에서도 이렇게 3분류로 나눠서 하는 게 좋음.
  • 실습은 편의상 트레이닝셋, 밸리데이션셋만 진행함.

 

1-7. 실습 환경 소개 (Colab)
더보기

01. 실습 환경 소개 (Colab)

 

  • Colaboratory: 구글에서 만든 개발 환경. 구글 드라이브에 파일 올려놓고 웹상에서 직접 코드 실행 가능.
  • Colab은 Jupyter Notebook을 기반으로 함. 각 '메모장'은 셀로 구성되며 셀 단위로 코드 실행, 출력가능.
  • 데이터분석/머신러닝에 자주 쓰이는 다양한 패키지들이 기본적으로 설치되어 있으며 추가 설치도 가능함.

 

1-8. 간단한 선형 회귀 실습
더보기

01. 선형 회귀 실습

 

  • TensorFlow: 기본 머신러닝 프레임워크
  • Keras: TensorFlow보다 사용이 편해서 이걸로 많이 쓴다. 우리 실습에서도 앞으로 Keras 사용 예정.

 

  • TensorFlow 이용해서 직접 선형회귀 코드 실행해보기

 

  • 1. 텐서플로우를 임포트하고 데이터와 변수들을 설정함.
import tensorflow as tf

tf.compat.v1.disable_eager_execution() # tf.compat.v1: 버전 1로 설정

x_data = [[1, 1], [2, 2], [3, 3]]
y_data = [[10], [20], [30]]

# placeholder: X, Y 값을 넣어주는 공간. 데이터 형태 지정필요. ex) float32
X = tf.compat.v1.placeholder(tf.float32, shape=[None, 2])
Y = tf.compat.v1.placeholder(tf.float32, shape=[None, 1])

W = tf.Variable(tf.random.normal(shape=(2, 1)), name='W')
b = tf.Variable(tf.random.normal(shape=(1,)), name='b')

 

  • 2. 가설과 비용함수, optimizer를 정의함.
hypothesis = tf.matmul(X, W) + b # session: 텐서플로우에서 사용하고 있는 모든 그래프와 변수의 저장소
cost = tf.reduce_mean(tf.square(hypothesis - Y))
optimizer = tf.compat.v1.train.GradientDescentOptimizer(learning_rate=0.01).minimize(cost)

 

  • 3. 매 스텝별로 결과를 출력하여 비용함수가 줄어드는 것을 확인함
  • 머신러닝에서는 매번 반복학습 시켜야 함.
  • 실제로는 Keras를 이용해 쉽게 선형회귀 실행가능
import numpy as np
from tensorflow.keras.models import Sequential # 모델 정의할 때 쓰는 클래스
from tensorflow.keras.layers import Dense # 위에서 썼던 가설을 구현할 때 사용함
from tensorflow.keras.optimizers import Adam, SGD

x_data = np.array([[1], [2], [3]]) # keras는 np.array를 입력으로 둔다.
y_data = np.array([[10], [20], [30]])

model = Sequential([ # 순차적으로 모델을 쌓아나갈 수 있도록 만든 구조
  Dense(1) # 선형회귀에서는 레이어가 하나이므로 출력도 하나라고 정의함.
])

model.compile(loss='mean_squared_error', optimizer=SGD(lr=0.1))
# 수식을 안 써도 된다는 점이 편함. SGD = Stochastic Gradient Descent

model.fit(x_data, y_data, epochs=100) # epochs(반복학습) 복수형으로 쓰기!
# 모델 정의가 끝났으므로 모델을 학습시킨다. 이 과정도 위와 비교하면 간단함.
# fit이라는 명령어만 사용하면 됨. 가설의 정답값을 맞춘다는 의미. 인자는 3개.
# 학습하는 진행상황이 나와서 좋음.


## 모델예측하기
y_pred = model.predict([[4]])
print(y_pred)

 

1-9. 캐글 선형회귀 실습
더보기

01. Kaggle 데이터 선형회귀 실습

 

  • Kaggle: 데이터 사이언티스트를 위한 커뮤니티
  • 세계에서 가장 많은 데이터셋을 보유하고 있음.
  • 다양한 데이터셋이 공개되어 있어 직접 분석, 결과 공유, 비교도 가능함.
  • 기업 및 단체에서 올린 문제를 풀어 상품 받을 수도 있음.

 

  • 캐글 데이터셋 직접 다운로드 받는 방법:
  • 1. 캐글 회원가입하기
  • 2. 로그인 > 내 프로필 > Account 탭 클릭
  • 3. API > Create New API Token > kaggle.json 파일 다운로드
  • 4. 브라우저에서 파일을 열어 username과 key 복사하기
  • 5. 환경변수 지정하기
import os
os.environ['KAGGLE_USERNAME'] = '[내_캐글_username]' # username
os.environ['KAGGLE_KEY'] = '[내_캐글_key]' # key
# csv : comma-separated values의 약자. 콤마로 구분되어 있는 값들이다.

 

  • 6. 원하는 데이터셋의 API를 복사해와 실행하기
!kaggle datasets download -d ashydv/advertising-dataset

 

  • 7. 데이터셋 압축 풀어주기
!unzip /content/advertising-dataset.zip

 

  • 데이터셋을 본격적으로 분석하는 방법:
  • 1. 필요한 라이브러리들 임포트하기
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.optimizers import Adam, SGD
import numpy as np # 여기까지는 기본적으로 사용하는 것들
import pandas as pd # csv 파일 읽을 때 사용
import matplotlib.pyplot as plt # 그래프 그릴 때 사용
import seaborn as sns # 그래프 그릴 때 사용
from sklearn.model_selection import train_test_split # 트레이닝셋, 테스트셋을 분류해주는 기능

 

  • 2. 데이터셋 불러와서 형태 확인하기
df = pd.read_csv('advertising.csv') # 자료 읽어오기
df.head(5) # 맨 앞에서 5줄 출력하는 명령어. df.tail(5): 뒤에서 5줄 출력해라

print(df.shape) # 데이터셋의 크기 살펴보는 법

 

  • 3. 데이터셋 살짝 살펴보기
sns.pairplot(df, x_vars=['TV', 'Newspaper', 'Radio'], y_vars=['Sales'], height=4)
# 상관관계를 한눈에 파악할 수 있는 pairplot을 보여줌

 

  • 4. 데이터셋 가공하기
x_data = np.array(df[['TV']], dtype=np.float32) # 인풋, 아웃풋을 항성 정의해야 함
y_data = np.array(df['Sales'], dtype=np.float32) # df에서 추출하고 np.array로 변환. Keras는 np.array 데이터만 취급. Tensorflow는 플로팅포인트 32비트 사용(데이터타입).

print(x_data.shape) # (200, 1)
print(y_data.shape) # (200,) 서로 크기가 안 맞으니 아래에서 통일함


x_data = x_data.reshape((-1, 1)) # -1: 남은 수만큼 알아서 변형. 1: 뒤에 무조건 1이 되어야 함
y_data = y_data.reshape((-1, 1)) # reshape: 데이터셋 자체는 변하지 않지만, 모양 맞추기

print(x_data.shape) # dense 형태로 맞출 때는 (n, 1) 형태로.
print(y_data.shape)

 

  • 5. 데이터셋을 학습 데이터와 검증 데이터로 분할하기
x_train, x_val, y_train, y_val = train_test_split(x_data, y_data, test_size=0.2, random_state=2021)
# 데이터셋 분할: 실습에서는 학습(80%), 검증(20%)로 분할. 실무에서는 학습, 검증, 테스트 데이터셋으로 분할.
# random_state: 랜덤 변수 지정하기. 섞어서 나누는데 그 섞는 시드가 된다.

print(x_train.shape, x_val.shape) # 학습: (160, 1) 검증: (40, 1)
print(y_train.shape, y_val.shape)

 

  • 6. 학습시키기
model = Sequential([
  Dense(1) # 출력이 1이기 때문. 세일즈만 예측하기
])

# 모델 구성하기, Adam: GradientDescentOptimizer과 동일하지만 보편적으로 성능이 더 좋음. 그냥 아담 쓰기.
model.compile(loss='mean_squared_error', optimizer=Adam(lr=0.1)) # lr: 러닝메이트

model.fit( # 학습시킬 때는 fit 사용.
    x_train,
    y_train,
    validation_data=(x_val, y_val), # 검증 데이터를 넣어주면 한 epoch이 끝날때마다 자동으로 검증
    # 한 에폭 끝날 때마다 검증된 데이터를 보여줌.
    epochs=100 # epochs 복수형으로 쓰기!

 

  • 7. 검증 데이터로 예측하기
y_pred = model.predict(x_val) # 예측은 model.predict 사용. 

plt.scatter(x_val, y_val) # 점을 다다다 찍는 그래프. 정답값
plt.scatter(x_val, y_pred, color='r') # 예측값 (빨간색) - 가설은 선형이므로 직선
plt.show() # 그래프를 보여주세요. 오밀조밀하게 잘 모여있어서 학습 잘된 것을 확인가능.

 

  • 8. 여러 X값을 이용하여 매출 예측하기
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.optimizers import Adam, SGD
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt 
import seaborn as sns
from sklearn.model_selection import train_test_split # 패키지는 위와 동일하게 불러오기

df = pd.read_csv('advertising.csv') # 데이터도 동일하게 pandas로 로드

x_data = np.array(df[['TV', 'Newspaper', 'Radio']], dtype=np.float32) # 입력값이 다름
y_data = np.array(df['Sales'], dtype=np.float32)

x_data = x_data.reshape((-1, 3)) # 뒤의 값이 3개니까
y_data = y_data.reshape((-1, 1))

print(x_data.shape) # (200, 3)
print(y_data.shape) # (200, 1)

x_train, x_val, y_train, y_val = train_test_split(x_data, y_data, test_size=0.2, random_state=2021)

print(x_train.shape, x_val.shape)
print(y_train.shape, y_val.shape)

model = Sequential([
  Dense(1) # 출력도 한 개
])

model.compile(loss='mean_squared_error', optimizer=Adam(lr=0.1))

model.fit(
    x_train,
    y_train,
    validation_data=(x_val, y_val), # 검증 데이터를 넣어주면 한 epoch이 끝날때마다 자동으로 검증
    epochs=100 # epochs 복수형으로 쓰기!
)
# 마지막에 loss, val_loss 확인하기. 
# initial weight가 랜덤으로 초기화되고, 최적화과정에서 값이 약간 변동있을 수도 있어서 값이 서로 다를 수 있음.

y_pred = model.predict(x_val) # 예측한 값을 보여줌
print(y_pred.shape) # (40, 1) # 3차원을 2차원으로 보여줘서 그래프가 선형이 아닌 것처럼 보임.

plt.scatter(x_val[:, 0], y_val) # 이런 식으로 3개 그려준다.
plt.scatter(x_val[:, 0], y_pred, color = 'r')
plt.show()

 

1-10. 1주차 끝 & 숙제 설명
더보기

01. 혼자서 Linear regression 구현하기

 

import os
os.environ['KAGGLE_USERNAME'] = 'username' # username
os.environ['KAGGLE_KEY'] = 'key' # key

!kaggle datasets download -d rsadiq/salary
!unzip salary.zip

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.optimizers import Adam, SGD
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt 
import seaborn as sns
from sklearn.model_selection import train_test_split

df = pd.read_csv('Salary.csv')

x_data = np.array(df['YearsExperience'], dtype=np.float32)
y_data = np.array(df['Salary'], dtype=np.float32)

x_data = x_data.reshape((-1, 1))
y_data = y_data.reshape((-1, 1))

print(x_data.shape)
print(y_data.shape)

x_train, x_val, y_train, y_val = train_test_split(x_data, y_data, test_size=0.2, random_state=2021)

print(x_train.shape, x_val.shape)
print(y_train.shape, y_val.shape)

model = Sequential([
  Dense(1)
])

model.compile(loss='mean_squared_error', optimizer=SGD(lr=0.01))

model.fit(
    x_train,
    y_train,
    validation_data=(x_val, y_val), # 검증 데이터를 넣어주면 한 epoch이 끝날때마다 자동으로 검증
    epochs=100 # epochs 복수형으로 쓰기!
)



y_pred = model.predict(x_val)

plt.scatter(x_val, y_val)
plt.scatter(x_val, y_pred, color='r')
plt.show()
Comments