최근에 실기 문제 유형의 개편이 있었는데, 그 부분을 확실히 담은 최신판이었고, 가장 많이 쓰는 언어는 Python으로 시험을 준비하는 사람들은 위한 버전이었습니다.
이기적 수험서의 장점은 다양한 학습자교의 제공입니다. 계획 없는 P라도 이기적에서 제공해주는 학습 플랜만 따라간다면 누구나 합격할 수 있을 것 같습니다. 위의 캘린더, 스터디 플래너, 오답노트 뿐만 아니라, <이기적 스터디 카페>에 가입하면 Q&A 서비스, 교재 구매 인증 시 추가 학습 자료, 동영상 강의(필기/실기) 까지 무료로 제공하고 있습니다. 이 정도의 학습자로라면 누구나 독학으로 합격할 수 있을 것 같습니다.
실기시험을 준비할 때 가장 걱정되는 것이 실제 시험환경은 어떨지에 관한 것일 건데요. 이 책에는 그 부분도 정확하게 설명해두어서, 실제 시험장에 들어가기 전 걱정을 덜 수 있을 것 같습니다.
파이썬을 베이스로 한 수험서로, 데이터 분석에 필요한 파이썬 코드 뿐만아니라, 파이썬의 기본 코드도 설명해주고 있어서, 파이썬이 완전 처음이신 분이라도 누구나 학습을 시작하기에 어려움이 없을 것 같습니다.
마지막으로 실기시험 준비에 가장 중요한 모의고사를 직접 풀어볼 수 있는 과정이 포함되어 있습니다.해설도 자세히 나와있어서 문제를 어떻게 풀어야할 지 이해하기에 너무나도 쉬웠습니다.
다들 <이기적 빅데이터분석기사 실기 기본서>로 이번 빅데이터 분석기사 실기 시험에 붙어봐요.
테이블로부터 회사 코드 | 설립자 | Lead Manager 총원 | Senior Manager 총원 | Manager 총원 | Employee 총원 을 계산한 테이블을 출력하고, 회사 코드 기준으로 오름차순 정렬
풀이
1. Employee 테이블에 설립자를 제외한 각 직급별 데이터가 다 나와 있으므로, Employee 테이블과 Company 테이블만 JOIN하여 COUNT
SELECT
C.company_code,
C.founder,
COUNT(DISTINCT E.lead_manager_code),
COUNT(DISTINCT E.senior_manager_code),
COUNT(DISTINCT E.manager_code),
COUNT(DISTINCT E.employee_code)
FROM Company C
LEFTJOIN Employee E ON C.company_code = E.company_code
GROUPBY
C.company_code,
C.founder
ORDERBY C.company_code
-- 출력
C1 Angela 12513
C10 Earl 1123
C100 Aaron 12410
C11 Robert 1111
C12 Amy 12614
C13 Pamela 12514
C14 Maria 1135
C15 Joe 1123
C16 Linda 1135
C17 Melissa 1237
C18 Carol 1256
C19 Paula 1247
C2 Frank 1113
C20 Marilyn 1122
C21 Jennifer 1137
C22 Harry 1136
C23 David 1112
C24 Julia 1126
C25 Kevin 1125
C26 Paul 1113
C27 James 1113
C28 Kelly 1259
C29 Robin 1249
C3 Patrick 1225
C30 Ralph 1125
C31 Gloria 1113
C32 Victor 1248
C33 David 12512
C34 Joyce 12610
C35 Donna 12612
C36 Michelle 12511
C37 Stephanie 1125
C38 Gerald 1246
C39 Walter 1137
C4 Lisa 1111
C40 Christina 1136
C41 Brandon 1237
C42 Elizabeth 1248
C43 Joseph 1246
C44 Lawrence 1134
C45 Marilyn 1113
C46 Lori 1239
C47 Matthew 1234
C48 Jesse 1133
C49 John 1138
C5 Kimberly 1239
C50 Martha 1125
C51 Timothy 12512
C52 Christine 1122
C53 Anthony 1111
C54 Paula 1247
C55 Kimberly 1223
C56 Louise 1113
C57 Martin 1125
C58 Paul 1248
C59 Antonio 1124
C6 Bonnie 1126
C60 Jacqueline 1112
C61 Diana 1111
C62 John 12511
C63 Dorothy 1257
C64 Evelyn 1112
C65 Phillip 1248
C66 Evelyn 12411
C67 Debra 1113
C68 David 1259
C69 Willie 1113
C7 Michael 1112
C70 Brandon 1247
C71 Ann 12510
C72 Emily 1237
C73 Dorothy 1112
C74 Jonathan 1247
C75 Dorothy 1124
C76 Marilyn 12512
C77 Norma 12510
C78 Nancy 1237
C79 Andrew 1122
C8 Todd 1113
C80 Keith 1112
C81 Benjamin 1139
C82 Charles 1123
C83 Alan 1234
C84 Tammy 1113
C85 Anna 1248
C86 James 1135
C87 Robin 1235
C88 Jean 1123
C89 Andrew 1247
C9 Joe 1136
C90 Roy 1123
C91 Diana 1222
C92 Christina 1113
C93 Jesse 1122
C94 Joyce 12513
C95 Patricia 1135
C96 Gregory 1122
C97 Brian 1111
C98 Christine 1125
C99 Lillian 1126
2. Employee 테이블에는 Lead Manager나 Senior Manager, Manager 중 하위 직급이 없는 사람에 대한 정보가 없으므로 완벽한 정보를 가지지 않을 수 있으므로, 상위 직급의 테이블도 연쇄적으로 JOIN하여 누락을 방지할 필요가 있음
SELECT
C.company_code,
C.founder,
COUNT(DISTINCT L.lead_manager_code),
COUNT(DISTINCT S.senior_manager_code),
COUNT(DISTINCT M.manager_code),
COUNT(DISTINCT E.employee_code)
FROM Company C
LEFTJOIN Lead_Manager L ON C.company_code = L.company_code
LEFTJOIN Senior_Manager S ON L.company_code = S.company_code
LEFTJOIN Manager M ON L.company_code = M.company_code
LEFTJOIN Employee E ON L.company_code = E.company_code
GROUPBY
C.company_code,
C.founder
ORDERBY C.company_code
-- 출력
C1 Angela 12513
C10 Earl 1123
C100 Aaron 12410
C11 Robert 1111
C12 Amy 12614
C13 Pamela 12514
C14 Maria 1135
C15 Joe 1123
C16 Linda 1135
C17 Melissa 1237
C18 Carol 1256
C19 Paula 1247
C2 Frank 1113
C20 Marilyn 1122
C21 Jennifer 1137
C22 Harry 1136
C23 David 1112
C24 Julia 1126
C25 Kevin 1125
C26 Paul 1113
C27 James 1113
C28 Kelly 1259
C29 Robin 1249
C3 Patrick 1225
C30 Ralph 1125
C31 Gloria 1113
C32 Victor 1248
C33 David 12512
C34 Joyce 12610
C35 Donna 12612
C36 Michelle 12511
C37 Stephanie 1125
C38 Gerald 1246
C39 Walter 1137
C4 Lisa 1111
C40 Christina 1136
C41 Brandon 1237
C42 Elizabeth 1248
C43 Joseph 1246
C44 Lawrence 1134
C45 Marilyn 1113
C46 Lori 1239
C47 Matthew 1234
C48 Jesse 1133
C49 John 1138
C5 Kimberly 1239
C50 Martha 1125
C51 Timothy 12512
C52 Christine 1122
C53 Anthony 1111
C54 Paula 1247
C55 Kimberly 1223
C56 Louise 1113
C57 Martin 1125
C58 Paul 1248
C59 Antonio 1124
C6 Bonnie 1126
C60 Jacqueline 1112
C61 Diana 1111
C62 John 12511
C63 Dorothy 1257
C64 Evelyn 1112
C65 Phillip 1248
C66 Evelyn 12411
C67 Debra 1113
C68 David 1259
C69 Willie 1113
C7 Michael 1112
C70 Brandon 1247
C71 Ann 12510
C72 Emily 1237
C73 Dorothy 1112
C74 Jonathan 1247
C75 Dorothy 1124
C76 Marilyn 12512
C77 Norma 12510
C78 Nancy 1237
C79 Andrew 1122
C8 Todd 1113
C80 Keith 1112
C81 Benjamin 1139
C82 Charles 1123
C83 Alan 1234
C84 Tammy 1113
C85 Anna 1248
C86 James 1135
C87 Robin 1235
C88 Jean 1123
C89 Andrew 1247
C9 Joe 1136
C90 Roy 1123
C91 Diana 1222
C92 Christina 1113
C93 Jesse 1122
C94 Joyce 12513
C95 Patricia 1135
C96 Gregory 1122
C97 Brian 1111
C98 Christine 1125
C99 Lillian 1126
새로 생긴 경영정보시각화능력 자격증을 준비하기 위해 이기적을 찾았습니다. IT 관련 자격증을 준비할 때 언제나 찾아보던 이기적 출판사에서 나온 책이라 믿고 보기로 하였습니다.
경영정보시각화능력 자격증은 회계, 재무, 마케팅 등 경영과 관련된 정보와 데이터 관련 내용, 그리고 이런 정보들을 효과적으로 시각화하여 경영을 도울 수 있는 능력을 시험하는 자격증입니다. 데이터 분석을 진행하며 시각화는 정말 중요한 한 부분이라고 생각하여 자격증을 준비하며 시각화 능력을 기르는 것은 좋은 기회라고 생각하였습니다.
책의 두께는 별로 두껍지 않습니다. 자격증 시험은 딥하게 들어갈 필요없이 합격을 위해 필요한 부분만 있으면 된다고 생각하기 때문에 이런 두께는 환영입니다.
책의 목차는 다음과 같이 구성되어 있습니다.
이번에 처음 시행되는 시험이라 다소 생소할 수도 있는 경영정보시각화 능력 자격증에 대한 정보도 상세하게 설명해주어 더 검색할 필요없이 공부에만 집중할 수 있었습니다.
책에는 기본 이론은 물론 '기적의 Tip'을 페이지마다 설명해주어 놓치는 부분없이 공부할 수 있었습니다.
각 장의 마무리는 배운 개념을 체크할 수 있는 문제들로 구성되어 한 번 더 확인하고 복습할 수 있었습니다.
예상문제 또한 시험에 대한 정보가 없는 상황에서 큰 도움이 되는 파트였습니다. 이미 다른 IT 관련 자격증의 문제집을 담당해온 이기적 출판사에서 내준 예상문제라면 더 신뢰성 있어보입니다.
책의 마지막 장에는 기출 예상문제로 구성되어있습니다. 실제 시험과 비슷한 형식으로, 공부를 다 하고, 시험 친다고 생각하고 실전 연습을 해볼 수도 있고, 시험에 대한 정보가 없는 상황에서 가장 비슷하게 시험을 경험해볼 수 있을 것 같습니다.
이기적 경영정보시각화능력 자격증 필기 문제집과 함께 다같이 한 번에 합격할 수 있었으면 좋겠습니다~!
Jenny Ashley Meera Jane
Samantha Christeen Priya Julia
NULL Ketty NULL Maria
풀이
1. 직업 별 이름들을 서브쿼리로 추출해서 JOIN
- row_number() over(ORDER BY Name) 열 추가: 이름 오름차순 정렬 + JOIN 기준으로 하여, 빈 셀에 Null 삽입
- JOIN 방향은 이름 개수가 가장 많은 Professor 열 기준: 다른 열 기준 시 Professor의 이름이 잘리기 때문
SELECT d.Name, p.Name, s.Name, a.Name
FROM
(SELECT Name, row_number() over(ORDERBY Name) as r
FROM OCCUPATIONS
WHERE Occupation ='Doctor') AS d
RIGHTJOIN
(SELECT Name, row_number() over(ORDERBY Name) as r
FROM OCCUPATIONS
WHERE Occupation ='Professor') AS p
ON d.r = p.r
LEFTJOIN
(SELECT Name, row_number() over(ORDERBY Name) as r
FROM OCCUPATIONS
WHERE Occupation ='Singer') AS s
ON p.r = s.r
LEFTJOIN
(SELECT Name, row_number() over(ORDERBY Name) as r
FROM OCCUPATIONS
WHERE Occupation ='Actor') AS a
ON s.r = a.r
-- 출력
Aamina Ashley Christeen Eve
Julia Belvet Jane Jennifer
Priya Britney Jenny Ketty
NULL Maria Kristeen Samantha
NULL Meera NULLNULLNULL Naomi NULLNULLNULL Priyanka NULLNULL
2. 각 직업의 수를 세서 "There are a total of 3 doctors"와 같이 출력(직업 count 오름차순, 직업명 오름차순)
Sample Output
Ashely(P)
Christeen(P)
Jane(A)
Jenny(D)
Julia(A)
Ketty(P)
Maria(A)
Meera(S)
Priya(S)
Samantha(D)
There are a total of 2 doctors.
There are a total of 2 singers.
There are a total of 3 actors.
There are a total of 3 professors.
2. 각 직업의 수를 세서 "There are a total of 3 doctors"를 직업 수 오름차순, 직업명 오름차순으로 출력
SELECT CONCAT('There are a total of ', COUNT(Occupation), ' ', LOWER(Occupation), 's.')
FROM OCCUPATIONS
GROUPBY Occupation
ORDERBYCOUNT(Occupation), Occupation;
-- 출력
There are a total of3 doctors.
There are a total of4 actors.
There are a total of4 singers.
There are a total of7 professors.
3. 두 쿼리를 UNION으로 결합하여 한 줄로 출력하려고 했지만 MySQL에서 UNION을 사용할 때 ORDER BY가 적용되지 않아 오류 발생
SELECT CONCAT(Name, '(', SUBSTR(Occupation,1,1),')')
FROM OCCUPATIONS
ORDERBY Name
UNIONSELECT CONCAT('There are a total of ', COUNT(Occupation), ' ', LOWER(Occupation), 's.')
FROM OCCUPATIONS
GROUPBY Occupation
ORDERBYCOUNT(Occupation), Occupation
-- 출력-- ERROR 1064 (42000) at line 1: You have an error in your SQL syntax; check the manual-- that corresponds to your MySQL server version for the right syntax to use near 'UNION-- SELECT CONCAT('There are a total of ', COUNT(Occupation), ' ', LOWER(Occup' at line 4
4. 각 쿼리를 서브쿼리에 넣어 UNION 시도했지만 MySQL에서 UNION하면 ORDER BY로 한 정렬이 풀려버림
SELECT a.*FROM (SELECT CONCAT(Name, '(', SUBSTR(Occupation,1,1),')')
FROM OCCUPATIONS
ORDERBY Name) AS a
UNIONSELECT b.*FROM (SELECT CONCAT('There are a total of ', COUNT(Occupation), ' ', LOWER(Occupation), 's.')
FROM OCCUPATIONS
GROUPBY Occupation
ORDERBYCOUNT(Occupation), Occupation) AS b
-- 출력
Ashley(P)
Samantha(A)
Julia(D)
Britney(P)
Maria(P)
Meera(P)
Priya(D)
Priyanka(P)
Jennifer(A)
Ketty(A)
Belvet(P)
Naomi(P)
Jane(S)
Jenny(S)
Kristeen(S)
Christeen(S)
Eve(A)
Aamina(D)
There are a total of3 doctors.
There are a total of4 actors.
There are a total of4 singers.
There are a total of7 professors.
정답
5. 두 쿼리를 UNION으로 묶지않고 ";"로 각 쿼리를 끝내 두 번 출력하는 방법으로 변경
SELECT CONCAT(Name, '(', SUBSTR(Occupation,1,1),')')
FROM OCCUPATIONS
ORDERBY Name;
SELECT CONCAT('There are a total of ', COUNT(Occupation), ' ', LOWER(Occupation), 's.')
FROM OCCUPATIONS
GROUPBY Occupation
ORDERBYCOUNT(Occupation), Occupation;
-- 출력
Aamina(D)
Ashley(P)
Belvet(P)
Britney(P)
Christeen(S)
Eve(A)
Jane(S)
Jennifer(A)
Jenny(S)
Julia(D)
Ketty(A)
Kristeen(S)
Maria(P)
Meera(P)
Naomi(P)
Priya(D)
Priyanka(P)
Samantha(A)
There are a total of3 doctors.
There are a total of4 actors.
There are a total of4 singers.
There are a total of7 professors.
가중치 업데이트 →wj:=wj+Δwj →가중치 업데이트 값인 Δwj=η(y(i)−ˆy(i))x(i)j →η는 학습률로 0.0~0.1 사이의 값 →y(i)와 ˆy(i)는 각각 i번째 훈련 샘플의 정답 레이블, 예측 레이블
- 가중치 벡터의 모든 가중치는 동시에 업데이트
- 모든 가중치가 각자의 업데이트 값 Δwj에 의해 업데이트되기 전에 예측 레이블 ˆy(i)를 다시 계산하지 않음 → Δw0=η(y(i)−output(i)) → Δw1=η(y(i)−output(i))x(i)1 → Δw2=η(y(i)−output(i))x(i)2
- 퍼셉트론이 클래스 레이블을 정확히 예측하면 가중치 유지(업데이트 값은 0) → y(i)=−1,ˆy(i)=−1,Δwj=η(−1−(−1))x(i)j=0 → y(i)=1,ˆy(i)=1,Δwj=η(1−1)x(i)j=0 - 퍼셉트론이 클래스 레이블을 잘못 예측하면 가중치를 양성 또는 음성 타겟 클래스 방향으로 이동시킴 → y(i)=1,ˆy(i)=−1,Δwj=η(1−(−1))x(i)j=η(2)x(i)j → y(i)=−1,ˆy(i)=1,Δwj=η(−1−1)x(i)j=η(−2)x(i)j
- 가중치 업데이트는 x(i)j값에 비례하여, 이 값을 조절해 결정경계를 더 크게 움직이거나 더 작게 움직일 수 있음
- 퍼셉트론은 두 클래스가 선형적으로 구분되고 학습률이 충분히 작을 때만 수렴이 보장됨
- 선형 결정 경계로 나눌 수 없다면 훈련 데이터셋을 반복할 최대 횟수(epoch)를 지정해 분류 허용 오차 지정, 그렇지 않으면 퍼셉트론은 가중치 업데이트를 멈추지 않음
퍼셉트론 알고리즘
4. 파이썬으로 퍼셉트론 학습 알고리즘 구현
- 퍼셉트론 클래스 정의
import numpy as np
classPerceptron(object):def__init__(self, eta = 0.01, n_iter = 50, random_state = 1):
self.eta = eta
self.n_iter = n_iter
self.random_state = random_state
deffit(self, X, y):
rgen = np.random.RandomState(self.random_state) # 난수 생성기 객체 생성(격리된 시드 부여 가능, 이전과 동일한 랜덤 시드 재현 가능)
self.w_ = rgen.normal(loc = 0.0, scale = 0.01,
size = 1 + X.shape[1]) # 평균 0, 표준편차 0.01의 정규분포에서 훈련 데이터 개수(X.shape[1]) + 절편(1)만큼의 난수 생성
self.errors_ = [] # 훈련 중 error의 감소 확인을 위한 객체for _ inrange(self.n_iter):
errors = 0for xi, target inzip(X, y):
update = self.eta * (target - self.predict(xi)) # 가중치 w_j의 변화량
self.w_[1:] += update * xi # 각 훈련 데이터에 가중치값 곱하기
self.w_[0] += update # 가중치값 업데이트
errors += int(update != 0.0) # 업데이트 된 값이 0이 아니면(실제값과 예측값이 다르면) errors에 1 더함
self.errors_.append(errors) # 전체 훈련 데이터에서 예측값이 실제값과 다르게 나온 횟수 추가return self
# 최종입력인 Z 계산defnet_input(self, X):return np.dot(X, self.w_[1:]) + self.w_[0]
# 결정함수를 임계값인 0과 비교, 크거나 같으면 1, 작으면 -1로 반환defpredict(self, X):return np.where(self.net_input(X) >= 0, 1, -1)
- fit 메서드 내의 w_객체에 가중치를 초기화하여 선언 - 가중치가 0이 아니어야 학습률(eta)가 분류 결과에 영향을 줄 수 있어 0이 아닌 랜덤 값으로 초기화 - 가중치가 0이면 eta는 가중치 벡터의 방향이 아니라 크기에만 영향을 미침
- 붓꽃 데이터셋에서 퍼셉트론 훈련 - Perceptron은 이진분류이므로 setosa와 versicolor에 대해서만 분류 진행 - 또한, 특성은 첫번째 특성(꽃받침 길이)와 세번째 특성(꽃잎 길이)을 사용
import pandas as pd
import matplotlib.pyplot as plt
s = 'https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data'
df = pd.read_csv(s, header = None, encoding = 'utf-8')
# 처음 100개의 데이터(setosa 50개와 versicolor 50개)의 다섯번째 열인 예측 대상을 y로 선언
y = df.iloc[0:100, 4].values
# setosa이면 -1, versicolor이면 1로 변경
y = np.where(y == 'Iris-setosa', -1, 1)
# 처음 100개의 데이터에서 예측에 사용하고자 하는 특성인 첫번재 열과 세번째 열만 추출
X = df.iloc[0:100, [0, 2]].values
# 산점도로 시각화
plt.scatter(X[:50, 0], X[:50, 1], color = 'red', marker = 'o', label = 'setose')
plt.scatter(X[50:100, 0], X[50:100, 1], color = 'blue', marker = 'x', label = 'versicolor')
plt.xlabel('sepal length [cm]')
plt.ylabel('petal length [cm]')
plt.legend(loc = 'upper left')
plt.show()
# Perceptron 알고리즘 훈련
ppn = Perceptron(eta = 0.1, n_iter = 10)
ppn.fit(X, y)
# 훈련 과정에서 각 반복에서 나온 오차의 개수 시각화
plt.plot(range(1, len(ppn.errors_) + 1), ppn.errors_, marker = 'o')
plt.xlabel('Epochs')
plt.ylabel('Number of updates')
plt.show()
from matplotlib.colors import ListedColormap
# 결정경계 시각화 함수defplot_decision_regions(X, y, classifier, resolution = 0.02):
markers = ('s', 'x', 'o', '^', 'v')
colors = ('red', 'blue', 'lightgreen', 'gray', 'cyan')
cmap = ListedColormap(colors[:len(np.unique(y))])
# 두 특성의 최소값과 최대값에 각각 -1, +1을 한 값을 정의
x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max() + 1
x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max() + 1# 위에서 구한 최소값과 최대값을 시작으로 하는 meshgrid 생성(최소값과 최대값 사이에 0.02의 간격으로 데이터 생성)
xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, resolution),
np.arange(x2_min, x2_max, resolution))
# ravel 함수로 meshgrid를 1차원으로 평평하게 펴고, xx1, xx2를 각각 X, y로 Perceptron에 넣어 예측
Z = classifier.predict(np.array([xx1.ravel(), xx2.ravel()]).T)
# 예측된 값을 다시 meshgrid 형태로 변경
Z = Z.reshape(xx1.shape)
# contourf 함수로 등고선 그래프 그리기(등고선이 두 집단의 경계로 그려짐)# 분류된 값이 -1인 것과 1인 것을 각각 빨간색과 파란색으로 구분
plt.contourf(xx1, xx2, Z, alpha = 0.3, cmap = cmap)
plt.xlim(xx1.min(), xx1.max())
plt.ylim(xx2.min(), xx2.max())
# 훈련 데이터를 빨간색 네모 모양와 파란색 x모양으로 구분for idx, cl inenumerate(np.unique(y)):
plt.scatter(x = X[y == cl, 0], y = X[y == cl, 1],
alpha = 0.8, c = colors[idx], marker = markers[idx], label = cl, edgecolor = 'black')
plot_decision_regions(X, y, classifier = ppn)
plt.xlabel('sepal length [cm]')
plt.ylabel('petal length [cm]')
plt.legend(loc = 'upper left')
plt.show()
- OvR(One-versus-Resr)기법을 사용하면 이진 분류기로 다중 클래스 문제 해결 가능 →클래스마다 하나의 분류기를 훈련(각 클래스를 양성으로 분류하고 나머지는 음성으로 분류) →레이블이 없는 새로운 데이터 샘플 분류 시에는 클래스 레이블의 개수와 같은 n개의 분류기 사용 → 신뢰도가 가장 높은 클래스 레이블(최종 입력의 절댓값이 가장 큰 클래스)을 분류하려는 샘플의 레이블로 선택
- 퍼셉트론의 가장 큰 문제는 수렴 → 구 클래스가 선형적인 초평면으로 구분될 수 있을 때 수렴하지만, 선형 결정 경계로 완벽하게 구분되지 않으면 최대 epoch를 지정하지 않는한 가중치 업데이트가 멈추지 않음