초기 가중치 값의 중요성

가중치 초기값은 신경망 학습에 있어서 매우매우 중요하다. 잘 설정한 초기 가중치가 신경망 전체의 성능을 좌우한다고해도 과언이 아니기 때문이다.

 

가중치 초기값은 균일한 값으로 설정되면 안된다. 왜냐하면 오차역전파법에서 모든 가중치의 값이 똑같이 갱신되기 때문이다. 이렇게 되면 가중치를 여러개 갖는 의미가 없다. 따라서 가중치가 고른(대칭적인 구조) 상황을 막기 위해 가중치 초깃값은 균일하지 않은, 무작위의 값으로 설정되야 한다.

 

그렇다면 가중치 초기값은 단순히 무작위로만 만들어지면 되는걸까?

이에 대한 내용으로는 은닉층에서 사용하는 활성화함수(Sigmoid, ReLU)를 살펴봐야 한다.

 

 

 

Xavier 초깃값 (Sigmoid에서 사용)

가중치의 초깃값은 은닉층 활성화값들에 영향을 미친다.

 

표준편차 1인 정규분포로 가중치를 초기화 했을 때의 각 층의 활성화값 분포

 

표준편차가 1인 정규분포로 초기 가중치 분포를 지정한 경우, 각 층의 활성화값들이 0과 1에 치우쳐져 있다. Sigmoid 함수는 값이 0 또는 1에 치우쳐지면 기울기가 0이 되기 때문에, 역전파의 기울기 값이 점점 작아지다 사라진다. (미분이 0이니까) 이러한 문제점을 기울기 소실(Gradient Vanishing)이라고 한다. 따라서 이 초기 가중치값은 층이 깊어지면질수록 제대로 학습을 하지 못한다.

 

 

표준편차 0.01인 정규분포로 가중치를 초기화 했을 때의 각 층의 활성화값 분포

 

표준편차가 1인 경우와 달리 0 또는 1로 치우치진 않아 기울기 소실 문제가 일어나지는 않았지만 활성화값들이 0.5로 치우쳐진 것 또한 문제이다. 이는 다수의 뉴런의 거의 같은 값을 출력한다는 뜻으로 여러 뉴런을 갖는 이유가 없어지기 때문이다. 이러한 문제점을 표현력을 제한한다는 관점에서의 문제점이라고 한다.

 

결국 초기 가중치값은 활성화값들이 0 또는 1로 치우쳐지지 않게 하면서도 표현력을 제한하지 않도록 고르게 분포되도록 해야한다.

 

 

 

 

Sigmoid에 대해서 활성화값들을 고르게 분포되게 해주는 초기 가중치값은 Xavier 초깃값으로, Xavier 초깃값은 일반적인 딥러닝 프레임워크들이 표준적으로 사용하고 있다. Xavier 초깃값은 앞 계층의 노드가 n개일 때 표준편차가 1/sqrt(n)인 분포를 사용한다. Xavier 초깃값을 사용하면 앞 층에 노드가 많을수록 대상 노드의 초깃값으로 설정하는 가중치가 좁게 퍼진다. 쉽게 생각해 데이터가 적당히 퍼져있다는 말이다. 따라서 Xavier 초깃값은 Sigmoid 함수가 표현력의 제한도 받지 않으면서 효율적으로 학습할 수 있도록 한다.

 

Xavier 초깃값을 이용할 때의 각 층의 활성화값 분포

 

추가적으로 위의 이미지를 보면 층이 깊어질수록 약간씩 일그러지는 형태를 볼 수 있는데, 이러한 일그러짐은 Sigmoid함수 대신 tanh 함수를 사용하면 개선된다.

 

 

 

He 초깃값 (ReLU에서 사용)

Xavier 초깃값은 활성화 함수가 선형일 때 사용한다. Sigmoid 함수와 tanh 함수는 좌우가 대칭이기 때문에 중앙이 선형적이라고 볼 수 있기 때문에 Xavier 초깃값을 사용한다. 하지만 ReLU는 대칭적이지 않기 때문에 ReLU에 맞춰 초깃값을 설정해야한다. 이 초깃값이 He 초깃값이다. 사실 He 초깃값은 Xavier 초깃값과 크게 다르지는 않다. He 초깃값은 앞 계층의 노드가 n개일 때 표준편차가 sqrt(2/n)인 정규분포를 사용하기 때문이다. 이러한 이유는 ReLU는 음의 영역이 0이기 때문에 더 넓게 분포시켜야 했기 때문이라고 직관적으로 해석할 수 있다.

 

ReLU 사용시 가중치 초깃값에 따른 활성화값 분포 변화

 

위의 그림을 보면 ReLU도 표준편차가 0.01인 경우 0으로 치우쳐진다. 또한 Xavier 초깃값을 사용하면 초반에는 고르게 분포되다가 층이 깊어질수록 치우치기 시작한다. 이는 층이 더 깊어지면 표준편차가 0.01인 경우와 같이 기울기 소실 문제가 발생한다. He 초깃값을 보면 층이 깊어져도 균일하게 분포되어있기 때문에 학습이 잘 될 것으로 (역전파가 잘 될 것으로) 예상할 수 있다.

 

결론적으로 현재까지는 Sigmoid나 tanh 등의 S자 모양 곡선 활성화 함수를 사용할 경우 Xavier 초깃값을 사용하고, ReLU를 사용할 때는 He 초깃값을 사용하는 것이 가장 좋은 초기 가중치값이라고 볼 수 있다.

 

 

 

다양한 최적화 알고리즘

최적화란 매개변수의 최적값을 찾는 것을 의미한다. 지금껏 설명해온 최적화 기법은 SGD, 확률적 경사 하강법 뿐이었다. 하지만 SGD에는 단점이 있다. 필자는 이 단점을 해결해나가는 다양한 최적화 알고리즘에 대해 설명하고자 한다.

 

완벽한 설명

 

위의 이미지는 블로그에서 갖고왔다. 이미지가 너무 완벽하게 설명되어 있어서 사실 뭘 더 설명하기가 애매할 지경이다. 따라서 수식과 함께 최적화 알고리즘별 특징을 짚고 넘어가겠다. 아래 내용을 이해 못하겠으면 그냥 위의 이미지만 이해해도 됨 갓벽

 

 

 

경사하강법

 

W : 가중치 매개변수 / η : 학습률 / 미분값 : 손실함수 기울기

 

SGD에 의한 최적화 갱신 경로

 

경사하강법은 무작정 기울어진 방향으로 진행하기 때문에 상당히 무식하다 비효율적이다. 또한 그림에서처럼 지그재그로 탐색하는 근본 원인이 기울어진 방향이 본래의 최솟값과 다른 방향을 가리켜서라는 점 또한 SGD의 단점이다.

 

 

 

모멘텀 (Momentum)

모멘텀은 운동량을 뜻하는 단어로, 더 효율적인 탐색을 위해 물리 법칙을 구현한 것이다.

 

v : 속도 / 𝛼 : 마찰, 저항

 

v는 속도를 의미하며, 기울기 방향으로 힘을 받아 물체가 가속된다는 물리 법칙을 의미한다. 모멘텀 알고리즘을 최적화에 사용할 경우 가중치 매개변수는 공이 그릇 바닥을 구르는 듯한 움직임을 보인다.

 

모멘텀에 의한 최적화 갱신 경로

 

SGD와 비교하면 지그재그 정도가 덜하다.

 

 

 

AdaGrad

신경망 학습에서 η, 학습률은 매우 중요하다. 따라서 이 학습률을 효과적으로 정하는 것이 매우 중요하다. 학습률을 효과적으로 정하는 기술 중 하나로 학습률 감소라는 것이 있는데, 이는 학습을 진행하면서 학습률을 점차 줄여나가는 방법으로 처음에는 크게 학습하다가 점점 조금씩만 학습하게 되는 기술이다.

 

AdaGrad는 이러한 학습률 감소를 각각의 매개변수에 맞춰 적응적으로 학습률을 조정하며 학습을 진행한다.

 

h : 기존 기울기 값을 제곱하여 계속 더해줌

 

위의 식을 보면 알다시피, AdaGrad는 매개변수를 갱신할 때 1/sqrt(h)를 곱해 학습률을 조정한다. 이는 매개변수의 원소 중에서 크게 갱신된 (많이 움직인) 원소는 학습률이 낮아진다는 뜻으로, 각각의 매개변수마다 학습률 감소가 다 다르다.

 

AdaGrad에 의한 최적화 갱신 경로

 

y축 방향을 보면 기울기가 커 초반에는 크게 움직이지만, 움직임에 비례해 갱신 정도도 큰 폭으로 줄어들면서 효율적으로 최솟값을 향하는 것을 볼 수 있다.

 

 

 

RMSProp

AdaGrad는 과거의 기울기를 제곱하여 계속 더해가기 때문에 학습을 진행할수록 갱신 강도가 약해진다. 따라서 학습을 많이 반복하면 어느 순간 갱신량이 0이 되는 순간이 온다. 이러한 문제점을 개선하는 것이 RMSProp이다.

 

RMSProp는 과거의 기울기를 계속해서 누적하는 것이 아니라 먼 과거의 기울기는 서서히 잊고 새로운 기울기의 정보를 크게 반영한다. 이를 지수이동평균(EMA)이라 하며 과거 기울기의 반영 규모를 기하급수적으로 감소시킨다.

 

 

 

Adam

Adam은 모멘텀과 AdaGrad를 합친 것으로, 더 효율적으로 빨리 탐색할 수 있으며 하이퍼파라미터의 편향 보정 또한 진행된다는 특징이 있다.

 

Adam에 의한 최적화 갱신 경로

 

 

 

 

 

 

 

속도비교

 

속도 비교

 

 

 

 


 

 

 

 

 

어떤 갱신 방법이 좋느냐, 라고 묻는다면 모른다. 가 대답이다. 안타깝기 짝이 없는 인공지능 세계는 영화와 달라 수많은 알고리즘들 중 다양한 조합을 시도하며 알고리즘을 구성하는 수개의 하이퍼 파라미터들을 대입하여 최적의 결과를 찾는, 결론적으로는 완벽한 노가다의 세계이기 때문이다. 갓 블래스 유,,,

 

 

 

 

 

 

 

 

 

앞서 설명했던 SGD와 같은 것을 우리는 수치 미분이라고 한다. 수치 미분을 사용하는 이유는 오차를 줄여나가 신경망의 최적의 매개변수(가중치,편향)를 찾아가기 위해서이며, 더 자세하게 설명하자면 가중치의 매개변수에 대한 손실 함수의 기울기를 구하기 위해서이다.

 

수치 미분은 구현은 단순하지만 계산이 오래걸린다는 단점이 있다. 신경망은 수만개의 데이터를 수십개의 은닉층으로 학습시키기 때문에 계산이 빨라야 한다. 따라서 우리는 가중치 매개변수의 기울기를 효율적으로 계산하는 방법이 필요하다.

 

 

 

계산 그래프

딥러닝 학습서의 바이블 <밑바닥부터 시작하는 딥러닝>의 '오차역전파법'의 내용을 완벽하게 알려주는 블로그의 링크를 기재했으니 참고하길 바란다. 책이 너무 완벽해서 구입을 추천하지만 오차역전파법 자체만을 공부하고 싶다면 블로그에 기재된 내용만으로도 충분하며, 보충 설명이 필요할 경우 유튜브를 참고하길 바란다.

 

저자는 오차역전파법에 대해 설명할 때 수식보다는 계산 그래프를 활용한다. 그 이유는 우선 계산 그래프가 수식에 비해 상당히 직관적으로 받아들여지고 국소적 계산을 볼 수 있기 때문이다. 국소적 계산이란 뭘까?

 

 

이건 짧은 수식이니까 그나마 낫지만 오차역전파는 미분이기 때문에 수식보단 계산 그래프가 더 직관적이다.

 

계산 그래프 / 연산 : 노드(node) , 직선 : 엣지(edge)

 

 

 

국소적 계산

국소적 계산은 계산 전체에서 어떤 일이 벌어지든 상관없이 자신과 관련된 정보만으로 결과를 출력한다. 즉, 매우 복잡한 수식도 까보면 수많은 연산들의 합과 곱이며, 이 수많은 연산들을 하나씩만 놓고 보면 사실 별거없다. 라는게 국소적 계산의 뜻이다. 또한 국소적 계산은 연산 하나하나들의 결과를 보관할 수 있다. 따라서 우리는 전체로 향하는 과정을 간결하면서도 전부 볼 수 있게 된다. 이러한 국소적 계산의 효과 덕에 우리는 효율적으로 역전파를 구할 수 있다. 여기서 역전파란, 위의 계산 그래프를 볼 때 진행 방향(왼쪽에서 오른쪽)의 반대 방향(오른쪽에서 왼쪽)을 의미하며 순전파(→)의 역방향(←)으로 미분 값을 전달한다.

 

가령 우리가 사과값이 오를 때, 최종 금액이 얼마나 변화하는지를 알고 싶을 때 우리는 미분을 사용한다. 미분의 정의는 순간적인 변화량이기 때문이다. 따라서 역전파는 국소적 미분을 전달하며 각 변수의 미분을 효율적으로 구할 수 있게 도와준다.

 

 

 

연쇄법칙

z = (x+y)^2

위의 수식은 합성 함수를 보이기 위한 수식이다.

합성 함수란, 여러 함수로 구성된 함수이며 연쇄법칙은 합성 함수의 미분에 대한 성질이다.

 

연쇄 법칙이란 합성 함수의 미분은 합성 함수를 구성하는 각 함수의 미분의 곱으로 나타낼 수 있다는 것이다.

 

연쇄 법칙

 

이과생이라면 위의 수식이 익숙할 것이다. 기억에는 고등학교때 수학도 가끔은 이렇게 풀었던 것 같다. 어쨌든, 위의 수식을 풀어 말하자면 여러개의 변수로 이루어진 함수에서 몇몇 변수를 하나의 변수로 새롭게 생성해 각각을 미분하여 곱하는게 원래의 함수를 미분하는 것과 같다는 말이다. 이 연쇄 법칙이 계산 그래프를 활용하여 역전파를 이해하는 중요한 키워드가 될 것이기 때문에 최소한 위의 수식에 대해서는 확실히 이해하고 넘어가길 바란다. 복잡해 보이는 것에 비해 의외로 별 내용이 없다.

 

 

 

일반 연산(덧셈, 곳셈) 노드의 역전파

왼쪽 : 순전파 / 오른쪽 : 역전파

 

덧셈 노드의 역전파는 입력 값을 그대로 흘려보낸다.

 

왼쪽 : 순전파 / 오른쪽 : 역전파

 

곱셈 노드의 역전파는 순전파 때의 입력신호들을 서로 바꿔 곱해 흘려보낸다.

 

 

화살표 밑에 숫자를 넣어보길 바란다.

더보기

 

 

 

 

 

 

 

 

 

 

 

활성화 함수 역전파

ReLU 계층

ReLU의 역전파는 순전파의 입력인 x가 0보다 크면 상류값을 그대로 하류로 흘려보내고 x가 0보다 작으면 0을 보낸다.

 

왼쪽 : ReLU 수식 / 오른쪽 : ReLU 수식 미분

 

ReLU 계층의 역전파

 

 

 

Sigmoid 계층

Sigmoid같은 경우는 설명이 복잡하므로 유튜브를 참고하길 바란다.

결론적으로 Sigmoid의 역전파는 순전파의 출력 y만으로 계산할 수 있다.

 

Sigmoid 계층의 역전파

 

 

 

 

 

 

 

 

Affine 계층

Affine Transformation, 어파인 변환은 행렬의 곱을 의미한다. 신경망은 입력층에서 은닉층으로 데이터를 옮길 때 데이터와 가중치, 편향에 대해 행렬곱을 진행하였는데 이러한 처리를 Affine 계층이라고 한다. Affine 계층은 결론적으로 행렬의 곱을 진행하기 위해서 행렬의 차원을 일치시키는게 핵심이다.

 

Affine 계층의 역전파

 

Affine 계층이 이제껏 설명했던 역전파들과 다른 점은 '스칼라 값'이 아닌 '행렬 값'이라는 것이다. 따라서 Affine 계층의 역전파는 전치행렬과 미분된 행렬의 행렬곱이다. 여기서 왜 전치행렬을 곱하는지에 대한 이유자코비안 미분을 알아야한다. 어쨌든 역전파된 행렬곱을 하기 위해서는 전치행렬과 미분된 행렬의 위치를 잘 배치해야 한다.

 

 

 

배치(Batch)용 Affine 계층

이번에는 데이터를 N개 묶어 순전파하는 배치용 Affine 계층에 대해 생각해보자.

 

배치용 Affine 계층의 역전파

 

Batch는 데이터가 기존에 1x2 의 형상을 한 행렬이었다면 Nx2의 형상을 한 행렬로 변환됐을 뿐, 기존의 Affine 계층과 방식은 똑같다.

 

 

 

 

기존의 신경망을 Affine 계층과 활성화 함수 계층으로 나타내는 그림

 

 

 

 

 

 

 

 

 

 

 

출력층 활성화 함수의 역전파

Softmax

Softmax 계층 또한 수식 자체는 아주 많이 매우 복잡하다. 이에 대한 자세한 부분은 여기를 참고하길 바란다. 여기서는 결론만 말할건데 결국 Softmax의 역전파는 신경망의 출력값과 정답 레이블의 오차를 앞 계층에 전달한다는 것이다. 신경망 학습의 목적은 신경망의 출력(Softmax의 출력)이 정답 레이블과 가까워지도록 가중치 매개변수의 값을 조정하는 것(최적화 : Optimization)이다. 따라서 신경망의 출력과 정답 레이블의 오차를 효율적으로 앞 계층에 전달해야 한다.

 

간소화한 Softmax-with-Loss 계층의 계산 그래프

 

예를 들어보겠다. 정답 레이블이 [0 1 0]이고 Softmax 계층이 [0.3 0.2 0.5]를 출력했다고 하자. 이 Softmax의 출력값은 3번째 인덱스를 가장 높은 확률로 잡고 정답 레이블의 인덱스는 0.2로 예측하기 때문에 매우 나쁜 학습률을 보이고 있다. 이 때 Softmax의 역전파는 [0.3 -0.8 0.5]를 앞 계층에 전달한다. 그 결과 앞 계층들은 큰 오차를 통해 크게 재학습된다.

 

만약 정답 레이블이 [0 1 0]이고 Softmax 계층이 [0.01 0.99 0]일 경우 오차는 [0.01 -0.01 0]이다. 따라서 이런 경우는 오차가 매우 적으므로(학습을 잘 했으므로) 역전파를 통해 새롭게 학습하는 정도도 작아진다.

 

여기서 Softmax 함수(분류)의 손실 함수로 교차 엔트로피 오차를 사용하는 이유와, 항등 함수(회귀)의 손실 함수로 오차제곱합을 사용하는 이유가 나타난다. 각각의 함수에 손실 함수를 맞춰준 이유는 이 손실 함수들을 쓰면 각각의 함수들의 역전파 값이 예측값 - 정답 레이블로 말끔하게 떨어지기 때문이다.

 

 

 

 


 

 

 

지금까지 설명한 오차역전파법은 가중치 매개변수의 기울기를 구하는데 이용된다. 수치 미분으로 가중치 매개변수의 기울기를 구현할 수 있지만 수치 미분의 계산이 오래 걸리기 때문에 그보다 효율적인 오차역전파법을 사용했다. 만약 본인이 직접 오차역전파를 구현한다면 수치 미분의 결과와 오차역전파의 결과를 비교하여 검증하면 된다. 컴퓨터의 계산이 아주 정밀하진 않아서 두 값이 완벽하게 일치하긴 어렵지만 오차가 거의 0에 가까우면 잘 구현한 것이고, 차이가 너무 크다면 잘못 구현한 것이니 눈물을 머금고 잘 만들길 바란다.

 

 

 

 

 

 

 

 

 

인공 신경망

 

앞서 신경망을 구성하기 위해서 활성화 함수, 가중치 등이 필요하다는 것을 설명했다. 위의 그림은 인공 신경망이다. 왼쪽의 그림은 은닉층이 1개이고 오른쪽 그림은 은닉층이 3개이다. 위의 선만 봐도 알 수 있다시피 신경망은 깊이가 깊어질수록, 은닉층의 노드 수가 많으면 많을수록 계산이 복잡해진다. 하지만 우리는 데이터 한 두개를 학습시키는 것이 아니라 수천, 수만개의 데이터를 학습시킨다. 우리는 인간이 할 수 있는 일이지만 더 정확하면서도 빠르게 예측, 분류하기 위해 인공지능을 사용하고자 한다. 어떻게 해야 수많은 은닉층을 가진 신경망도 빠르게 데이터를 학습시킬 수 있을까?

 

 

 

배치(Batch)

 

우리는 인공신경망에 10만개의 데이터를 학습시키고자 한다. 이 10만개의 데이터를 어떻게 해야 빠르게 학습시킬 수 있을까? 컴퓨터의 핵심인 CPU는 결국에는 계산이 주요한 능력이기 때문에 계산이 매우 빠르다. 따라서 우리는 데이터를 1개씩 입력받아 총 10만번의 연산을 진행하는 것보다 한번에 큰 묶음으로 데이터로 입력받아(Batch) n번의 연산을 진행하는 것이 더 빠르다. 즉, 느린 I/O를 통해 데이터를 읽는 횟수를 줄이고 CPU나 GPU로 순수 계산을 하는 비율을 높여 속도를 빠르게 할 수 있다는 뜻이다. 또한 수치 계산 라이브러리 대부분이 큰 배열을 효율적으로 처리할 수 있도록 고도로 최적화되어 있기 때문에 Batch(묶음)로 데이터를 입력받아 학습시키는 것이 속도 측면에서 효율적이다.

 

그렇다면 미니배치 학습이라는 것은 무엇일까?

 

 

 

미니배치 (Mini-Batch)

미니배치를 설명하기에 앞서 데이터를 하나씩 학습시키는 방법과 전체를 학습시키는 방법의 장단점에 대해 소개하겠다.

 

데이터를 하나 하나씩 신경망에 넣어 학습을 시키면 우선 장점으로는 신경망을 한번 학습시키는데 소요되는 시간이 매우 짧다는 것이다. 하지만 데이터를 하나씩 입력받으면 계산 속도가 사실상 늦어진다. 우리는 대부분 딥러닝에 GPU를 많이 사용하는데 그 이유가 GPU의 병렬처리에 있다. 하지만 데이터를 한개씩 넣어 인공지능을 학습시키면 사실상 GPU의 병렬처리를 사용하지 않으니 그만큼 자원 낭비가 된다. 또한 오차를 줄이기 위해 사용하는 Loss Function에서 최적의 파라미터를 설정하는데 상당히 많이 헤매게 된다.

 

전체 데이터를 입력하는 경우는 한번에 여러개의 데이터에 대해서 신경망을 학습시킬 수 있으므로 오차를 줄일 수 있는 cost function의 최적의 parameter를 하나씩 학습하는것보다 빠르게 알아낼 수 있다. 하지만 전체 데이터를 학습시키기 때문에 신경망을 한 번 학습시키는데 소요되는 시간이 매우 길다.

 

위의 문제점과 장점을 적절히 섞은 것이 바로 미니 배치이다. 미니 배치는 SGD(Stochastic Gradient Descent : 확률적 경사 하강법)와 배치를 섞은 것으로 전체 데이터를 N등분하여 각각의 학습 데이터를 배치 방식으로 학습시킨다. 따라서 최대한 신경망을 한 번 학습시키는데(iteration) 걸리는 시간을 줄이면서 전체 데이터를 반영할 수 있게되며 효율적으로 CPU와 GPU를 활용할 수 있게 된다.

 

 

 

에폭 (Epoch)

Epoch는 인공 신경망에서 전체 데이터 셋에 대해 한 번 학습을 완료한 상태를 의미한다. 신경망은 최적의 가중치와 기타 파라미터를 찾기 위해서 역전파 알고리즘(backpropagation algorithm)을 사용한다. 역전파에는 파라미터를 사용하여 입력부터 출력까지의 각 계층의 weight을 계산하는 과정인 순방향과 반대로 거슬러 올라가며 다시 한 번 계산 과정을 거쳐 기존의 weight을 수정하는 역방향으로 나뉜다. 이 순방향과 역방향을 모두 완료하면 한 번의 Epoch가 완료됨을 의미한다. 더 쉽게 말하자면 1에폭은 학습에서 훈련 데이터를 모두 소진했을 때의 횟수에 해당한다.

 

딥러닝 모델 훈련과정 도식화 / 출처 : https://www.kakaobrain.com/blog/113

위의 그림에서 손실함수라는 것은 Loss Function, 즉 Cost Function을 의미한다.

 

예를 들어 훈련 데이터 10000개를 100개의 미니배치로 학습할 경우, 최적의 가중치를 구하기 위해 SGD를 100번 반복해야 모든 훈련 데이터를 학습에 활용하기 때문에 100회가 1에폭이 된다.

 

위의 그림의 손실함수(Loss Function)에 대해 더 자세히 설명하자면 손실함수란 Loss Function, 혹은 Cost Function이라고도 하며, 신경망에서 내놓는 결과값과 실제 결과값 사이의 차이를 정의하는 함수이다. 우리는 이 손실함수의 값이 최소값이어야 가장 좋은 신경망을 만들었다고 할 수 있다. 가장 많이 쓰는 손실 함수로 오차 제곱합(Sum of Sqaures for Error)와 교차 엔트로피 오차(Cross Entropy Error)가 있으며, 오차 제곱 합은 회귀에 주로 사용하고, 교차 엔트로피 오차는 분류에 주로 사용한다.

 


 

그리고 필자는 처음에 미니배치를 공부할 때 수만건의 데이터 중에서 n개만큼의 데이터를 임의로 추출하기 때문에 당연히 복원 추출이라고 생각했는데 (왜냐하면 데이터 개수가 매우 많으면서 데이터 전체를 표현해야하기 때문에), Epoch의 개념에서는 전체 데이터셋을 사용해야 1Epoch가 된다는 것을 읽으며 골머리를 앓았다.

배치, 혹은 미니배치학습을 할 때 배치는 복원 추출인가? 비복원 추출인가?

 

이에 대한 해답을 정확히 알 수는 없었으나, 어쨌든 복원 추출이나 비복원 추출이나 사실상 크게 다르지 않다는게 결론이다. 이에 대한 이유는 데이터가 매우 많기 때문이다. 수많은 데이터 중에서 임의의 개수로 임의의 데이터를 추출하는 것을 n번 반복하면 사실상 비복원 추출이나 복원 추출이나 전체 데이터셋을 사용한다고 볼 수 있기 때문이다.

 

 

 

확률적 경사 하강법 (SGD : Stochastic Gradient Descent)

SGD는 신경망을 학습할 때마다 가중치를 갱신한다. 기존의 경사 하강법과 개념이 다르지 않은데, 확률적이라는 말이 앞에 붙은 이유는 학습할 때마다 가중치를 갱신하기 때문에 학습할 때마다 신경망의 성능이 들쑥날쑥 변하면서 정답에 가까워지기 때문에 '무작위적'으로 보이기 때문이다. 

 

신경망에서 경사하강법을 사용하면 왼쪽의 그림처럼 정확도가 들쑥날쑥하기 때문에 확률적 경사 하강법이라고 한다.

 

SGD이외에도 배치에 대한 자세한 설명과 BGD에 대해 알고 싶다면 블로그를 참고하길 바란다.

 

 

 

 

 

 

항등함수와 Softmax 함수

 

 

항등함수와 Softmax함수는 출력층에서 사용하는 활성화 함수로, 항등함수 (identity function)는 입력을 그대로 출력한다.

그렇다면 위의 그림에서 항등 함수 옆에 있는 Softmax 함수는 무엇일까? Softmax 함수는 그림에서 보다시피 모든 입력 신호로부터 영향을 받는다.

 

Softmax 함수 식

 

Softmax 함수의 분자는 입력 신호 a_k의 지수함수, 분모는 모든 입력 신호의 지수 함수의 합으로 구성된다.

이렇게 모든 입력 신호로부터 영향을 받는 이유는 Softmax는 확률적 해석이 가능하게 하는 출력층의 활성화 함수로 사용되기 때문이다. 이와 같은 특징 때문에 항등함수는 회귀에서 주로 사용하고, Softmax 함수는 분류에 주로 사용한다.

 

 

 

 

 


 

 

 

Softmax 함수

오버플로우 문제

Softmax는 분류 문제를 풀 때 값을 확률로 변환하기 위해 사용하는 함수로, 신경망을 학습할 때에는 Softmax를 사용하지만 추론(순전파), 즉 실제 test data에 모델을 적용할 때는 Softmax 함수를 생략하는 것이 일반적이다. Softmax 함수는 지수(e) 함수를 사용하기 때문에 값이 기하급수적으로 커져 컴퓨터에서 inf, 무한대로 인식하기가 쉽다. 따라서 이러한 큰 값들끼리 나눗셈을 하면 수치가 불안정해지는 오버플로우 문제가 발생한다. 따라서 위의 Softmax 함수 식을 그대로 프로그래밍하면 오버플로우가 발생할 수 있으므로 우리는 위의 식을 개선할 필요가 있다.

 

 

개선한 Softmax 함수 식

 

위의 식이 개선된 Softmax 함수 식이다. 이 수식을 쉽게 말하자면 임의의 숫자 C를 분모 분자에 모두 곱하여 기하급수적으로 커지는 지수함수의 값을 작게 만들어서 오버플로우를 방지한다는 뜻이다. 이 때 임의의 숫자 C'는 입력신호(a_k)의 최대값을 음수로 변환한 값이다.

 

즉, 예를 들어 입력신호 a_k가 1000 800 500 이렇게 3개가 있다면 C' 은 -1000 이고, 위의 개선된 Softmax 함수 수식에 a_k+C'을 대입하면 0 -200 -500이 된다.

이 값들은 지수함수의 성질에 의해 매우 작은 숫자로 반환되어 오버플로우가 발생하지 않는다.

 

 

특징

Softmax 함수의 출력은 0에서 1 사이의 실수이며, Softmax 함수의 출력의 총합은 1이다. 이러한 성질 때문에 Softmax 함수의 출력을 '확률'로 해석할 수 있다. 또한 Softmax는 분류 문제에 주로 쓰이므로 분류 개수만큼의 출력층의 뉴런수를 잘 설정해야한다. 예를 들어 고양이, 개로 분류하고자 하면 출력층의 뉴런수를 2개로 설정하면 되고 숫자를 0부터 9까지 분류하고자 하면 출력층의 뉴런수를 10개로 설정하면 된다.

 

 

 

 

 


 

 

 

 

 

출처 : https://wikidocs.net/35476

 

위의 그림은 Softmax 함수를 가장 직관적으로 이해할 수 있는 그림이다. 출처의 링크를 참고하길 바란다.

 

출처 : https://wikidocs.net/35476

 

앞서 기술했다시피, Softmax 함수에 의해 출력된 값들은 0.26 0.70 0.04처럼 0에서 1사이의 값이며 총합이 1이므로 출력값을 확률로 받아들일 수 있다. 신경망을 학습할 때 Softmax의 예측값과 실제값의 오차를 계산하여 이를 통해 가중치와 편향을 업데이트하며(역전파) 신경망을 만든다. 위의 그림의 실제값은 0 1 0 의 형태인 원-핫 인코딩 형태로 원-핫 인코딩이란 정답을 뜻하는 원소만 1이고 나머지는 모두 0인 배열을 의미한다.

 

 

 

 

 

 

 

[Pandas] 문자열(str) 처리법

Pandas에는 문자열을 다루는 여러 함수가 존재합니다.e메일 주소를 이용하여 어떤 종류들의 함수가 있는지...

blog.naver.com

 

판다스에서 문자열 처리하려면 .str을 써야한다.

 

 

 

판다스 정리4

분할

In [25]:
from IPython.core.display import display, HTML
display(HTML("<style>.container { width:90% !important;}</style>"))
In [10]:
import pandas as pd

emp = pd.read_csv("c:/data/emp3.csv")
emp
Out[10]:
index empno ename job mgr hiredate sal comm deptno
0 1 7839 KING PRESIDENT NaN 1981-11-17 0:00 5000 NaN 10
1 2 7698 BLAKE MANAGER 7839.0 1981-05-01 0:00 2850 NaN 30
2 3 7782 CLARK MANAGER 7839.0 1981-05-09 0:00 2450 NaN 10
3 4 7566 JONES MANAGER 7839.0 1981-04-01 0:00 2975 NaN 20
4 5 7654 MARTIN SALESMAN 7698.0 1981-09-10 0:00 1250 1400.0 30
5 6 7499 ALLEN SALESMAN 7698.0 1981-02-11 0:00 1600 300.0 30
6 7 7844 TURNER SALESMAN 7698.0 1981-08-21 0:00 1500 0.0 30
7 8 7900 JAMES CLERK 7698.0 1981-12-11 0:00 950 NaN 30
8 9 7521 WARD SALESMAN 7698.0 1981-02-23 0:00 1250 500.0 30
9 10 7902 FORD ANALYST 7566.0 1981-12-11 0:00 3000 NaN 20
10 11 7369 SMITH CLERK 7902.0 1980-12-09 0:00 800 NaN 20
11 12 7788 SCOTT ANALYST 7566.0 1982-12-22 0:00 3000 NaN 20
12 13 7876 ADAMS CLERK 7788.0 1983-01-15 0:00 1100 NaN 20
13 14 7934 MILLER CLERK 7782.0 1982-01-11 0:00 1300 NaN 10
In [11]:
count, bin_dividers = np.histogram(emp.sal,bins=3)
print(count)
print(bin_dividers) # 경계값 리스트
[8 5 1]
[ 800. 2200. 3600. 5000.]
In [12]:
bin_names = ['저소득','중간소득','고소득']
emp['sal_divide'] = pd.cut(x=emp.sal,bins=bin_dividers,labels=bin_names)
emp
Out[12]:
index empno ename job mgr hiredate sal comm deptno sal_divide
0 1 7839 KING PRESIDENT NaN 1981-11-17 0:00 5000 NaN 10 고소득
1 2 7698 BLAKE MANAGER 7839.0 1981-05-01 0:00 2850 NaN 30 중간소득
2 3 7782 CLARK MANAGER 7839.0 1981-05-09 0:00 2450 NaN 10 중간소득
3 4 7566 JONES MANAGER 7839.0 1981-04-01 0:00 2975 NaN 20 중간소득
4 5 7654 MARTIN SALESMAN 7698.0 1981-09-10 0:00 1250 1400.0 30 저소득
5 6 7499 ALLEN SALESMAN 7698.0 1981-02-11 0:00 1600 300.0 30 저소득
6 7 7844 TURNER SALESMAN 7698.0 1981-08-21 0:00 1500 0.0 30 저소득
7 8 7900 JAMES CLERK 7698.0 1981-12-11 0:00 950 NaN 30 저소득
8 9 7521 WARD SALESMAN 7698.0 1981-02-23 0:00 1250 500.0 30 저소득
9 10 7902 FORD ANALYST 7566.0 1981-12-11 0:00 3000 NaN 20 중간소득
10 11 7369 SMITH CLERK 7902.0 1980-12-09 0:00 800 NaN 20 NaN
11 12 7788 SCOTT ANALYST 7566.0 1982-12-22 0:00 3000 NaN 20 중간소득
12 13 7876 ADAMS CLERK 7788.0 1983-01-15 0:00 1100 NaN 20 저소득
13 14 7934 MILLER CLERK 7782.0 1982-01-11 0:00 1300 NaN 10 저소득

더미변수

In [13]:
pd.get_dummies(emp.deptno)
Out[13]:
10 20 30
0 1 0 0
1 0 0 1
2 1 0 0
3 0 1 0
4 0 0 1
5 0 0 1
6 0 0 1
7 0 0 1
8 0 0 1
9 0 1 0
10 0 1 0
11 0 1 0
12 0 1 0
13 1 0 0

문자형을 날짜형으로 변환

In [14]:
df = pd.read_csv("c:/data/studyfile/stock-data.csv")
print(df.info())
df.head()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 20 entries, 0 to 19
Data columns (total 6 columns):
 #   Column  Non-Null Count  Dtype 
---  ------  --------------  ----- 
 0   Date    20 non-null     object
 1   Close   20 non-null     int64 
 2   Start   20 non-null     int64 
 3   High    20 non-null     int64 
 4   Low     20 non-null     int64 
 5   Volume  20 non-null     int64 
dtypes: int64(5), object(1)
memory usage: 1.1+ KB
None
Out[14]:
Date Close Start High Low Volume
0 2018-07-02 10100 10850 10900 10000 137977
1 2018-06-29 10700 10550 10900 9990 170253
2 2018-06-28 10400 10900 10950 10150 155769
3 2018-06-27 10900 10800 11050 10500 133548
4 2018-06-26 10800 10900 11000 10700 63039
In [15]:
df[['Date']] = pd.to_datetime(df.Date)
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 20 entries, 0 to 19
Data columns (total 6 columns):
 #   Column  Non-Null Count  Dtype         
---  ------  --------------  -----         
 0   Date    20 non-null     datetime64[ns]
 1   Close   20 non-null     int64         
 2   Start   20 non-null     int64         
 3   High    20 non-null     int64         
 4   Low     20 non-null     int64         
 5   Volume  20 non-null     int64         
dtypes: datetime64[ns](1), int64(5)
memory usage: 1.1 KB
In [16]:
df.Date.dt.year.tail()
Out[16]:
15    2018
16    2018
17    2018
18    2018
19    2018
Name: Date, dtype: int64
In [17]:
df.Date.dt.month.head()
Out[17]:
0    7
1    6
2    6
3    6
4    6
Name: Date, dtype: int64
In [18]:
df.Date.dt.day.head()
Out[18]:
0     2
1    29
2    28
3    27
4    26
Name: Date, dtype: int64

인덱스를 날짜형으로 만들기

In [19]:
df.set_index('Date',inplace=True)
df
Out[19]:
Close Start High Low Volume
Date
2018-07-02 10100 10850 10900 10000 137977
2018-06-29 10700 10550 10900 9990 170253
2018-06-28 10400 10900 10950 10150 155769
2018-06-27 10900 10800 11050 10500 133548
2018-06-26 10800 10900 11000 10700 63039
2018-06-25 11150 11400 11450 11000 55519
2018-06-22 11300 11250 11450 10750 134805
2018-06-21 11200 11350 11750 11200 133002
2018-06-20 11550 11200 11600 10900 308596
2018-06-19 11300 11850 11950 11300 180656
2018-06-18 12000 13400 13400 12000 309787
2018-06-15 13400 13600 13600 12900 201376
2018-06-14 13450 13200 13700 13150 347451
2018-06-12 13200 12200 13300 12050 558148
2018-06-11 11950 12000 12250 11950 62293
2018-06-08 11950 11950 12200 11800 59258
2018-06-07 11950 12200 12300 11900 49088
2018-06-05 12150 11800 12250 11800 42485
2018-06-04 11900 11900 12200 11700 25171
2018-06-01 11900 11800 12100 11750 32062
In [20]:
df.index
Out[20]:
DatetimeIndex(['2018-07-02', '2018-06-29', '2018-06-28', '2018-06-27',
               '2018-06-26', '2018-06-25', '2018-06-22', '2018-06-21',
               '2018-06-20', '2018-06-19', '2018-06-18', '2018-06-15',
               '2018-06-14', '2018-06-12', '2018-06-11', '2018-06-08',
               '2018-06-07', '2018-06-05', '2018-06-04', '2018-06-01'],
              dtype='datetime64[ns]', name='Date', freq=None)
In [21]:
df.index.year
Out[21]:
Int64Index([2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018,
            2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018],
           dtype='int64', name='Date')
In [22]:
df.index.month
Out[22]:
Int64Index([7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6], dtype='int64', name='Date')
In [23]:
df.index.day
Out[23]:
Int64Index([2, 29, 28, 27, 26, 25, 22, 21, 20, 19, 18, 15, 14, 12, 11, 8, 7, 5,
            4, 1],
           dtype='int64', name='Date')
판다스 정리3
In [1]:
from IPython.core.display import display, HTML
display(HTML("<style>.container { width:90% !important;}</style>"))

조건

Query 사용 and DataFrame[[컬럼명]][조건]

In [12]:
import pandas as pd

emp = pd.read_csv("c:/data/emp3.csv")
emp
Out[12]:
index empno ename job mgr hiredate sal comm deptno
0 1 7839 KING PRESIDENT NaN 1981-11-17 0:00 5000 NaN 10
1 2 7698 BLAKE MANAGER 7839.0 1981-05-01 0:00 2850 NaN 30
2 3 7782 CLARK MANAGER 7839.0 1981-05-09 0:00 2450 NaN 10
3 4 7566 JONES MANAGER 7839.0 1981-04-01 0:00 2975 NaN 20
4 5 7654 MARTIN SALESMAN 7698.0 1981-09-10 0:00 1250 1400.0 30
5 6 7499 ALLEN SALESMAN 7698.0 1981-02-11 0:00 1600 300.0 30
6 7 7844 TURNER SALESMAN 7698.0 1981-08-21 0:00 1500 0.0 30
7 8 7900 JAMES CLERK 7698.0 1981-12-11 0:00 950 NaN 30
8 9 7521 WARD SALESMAN 7698.0 1981-02-23 0:00 1250 500.0 30
9 10 7902 FORD ANALYST 7566.0 1981-12-11 0:00 3000 NaN 20
10 11 7369 SMITH CLERK 7902.0 1980-12-09 0:00 800 NaN 20
11 12 7788 SCOTT ANALYST 7566.0 1982-12-22 0:00 3000 NaN 20
12 13 7876 ADAMS CLERK 7788.0 1983-01-15 0:00 1100 NaN 20
13 14 7934 MILLER CLERK 7782.0 1982-01-11 0:00 1300 NaN 10
In [14]:
emp.query('sal<=3000')
emp[emp.sal<=3000]
Out[14]:
index empno ename job mgr hiredate sal comm deptno
1 2 7698 BLAKE MANAGER 7839.0 1981-05-01 0:00 2850 NaN 30
2 3 7782 CLARK MANAGER 7839.0 1981-05-09 0:00 2450 NaN 10
3 4 7566 JONES MANAGER 7839.0 1981-04-01 0:00 2975 NaN 20
4 5 7654 MARTIN SALESMAN 7698.0 1981-09-10 0:00 1250 1400.0 30
5 6 7499 ALLEN SALESMAN 7698.0 1981-02-11 0:00 1600 300.0 30
6 7 7844 TURNER SALESMAN 7698.0 1981-08-21 0:00 1500 0.0 30
7 8 7900 JAMES CLERK 7698.0 1981-12-11 0:00 950 NaN 30
8 9 7521 WARD SALESMAN 7698.0 1981-02-23 0:00 1250 500.0 30
9 10 7902 FORD ANALYST 7566.0 1981-12-11 0:00 3000 NaN 20
10 11 7369 SMITH CLERK 7902.0 1980-12-09 0:00 800 NaN 20
11 12 7788 SCOTT ANALYST 7566.0 1982-12-22 0:00 3000 NaN 20
12 13 7876 ADAMS CLERK 7788.0 1983-01-15 0:00 1100 NaN 20
13 14 7934 MILLER CLERK 7782.0 1982-01-11 0:00 1300 NaN 10
In [18]:
emp.query('job in ["SALESMAN"]')
emp[emp.job.isin(["SALESMAN"])]
Out[18]:
index empno ename job mgr hiredate sal comm deptno
4 5 7654 MARTIN SALESMAN 7698.0 1981-09-10 0:00 1250 1400.0 30
5 6 7499 ALLEN SALESMAN 7698.0 1981-02-11 0:00 1600 300.0 30
6 7 7844 TURNER SALESMAN 7698.0 1981-08-21 0:00 1500 0.0 30
8 9 7521 WARD SALESMAN 7698.0 1981-02-23 0:00 1250 500.0 30
In [20]:
emp.query('job not in ["SALESMAN"]')
emp[~emp.job.isin(['SALESMAN'])] # not in == ~
Out[20]:
index empno ename job mgr hiredate sal comm deptno
0 1 7839 KING PRESIDENT NaN 1981-11-17 0:00 5000 NaN 10
1 2 7698 BLAKE MANAGER 7839.0 1981-05-01 0:00 2850 NaN 30
2 3 7782 CLARK MANAGER 7839.0 1981-05-09 0:00 2450 NaN 10
3 4 7566 JONES MANAGER 7839.0 1981-04-01 0:00 2975 NaN 20
7 8 7900 JAMES CLERK 7698.0 1981-12-11 0:00 950 NaN 30
9 10 7902 FORD ANALYST 7566.0 1981-12-11 0:00 3000 NaN 20
10 11 7369 SMITH CLERK 7902.0 1980-12-09 0:00 800 NaN 20
11 12 7788 SCOTT ANALYST 7566.0 1982-12-22 0:00 3000 NaN 20
12 13 7876 ADAMS CLERK 7788.0 1983-01-15 0:00 1100 NaN 20
13 14 7934 MILLER CLERK 7782.0 1982-01-11 0:00 1300 NaN 10

대문자 소문자 바꾸기

In [34]:
emp[['ename']] = emp['ename'].str.lower()
emp
Out[34]:
index empno ename job mgr hiredate sal comm deptno
0 1 7839 king PRESIDENT NaN 1981-11-17 0:00 5000 NaN 10
1 2 7698 blake MANAGER 7839.0 1981-05-01 0:00 2850 NaN 30
2 3 7782 clark MANAGER 7839.0 1981-05-09 0:00 2450 NaN 10
3 4 7566 jones MANAGER 7839.0 1981-04-01 0:00 2975 NaN 20
4 5 7654 martin SALESMAN 7698.0 1981-09-10 0:00 1250 1400.0 30
5 6 7499 allen SALESMAN 7698.0 1981-02-11 0:00 1600 300.0 30
6 7 7844 turner SALESMAN 7698.0 1981-08-21 0:00 1500 0.0 30
7 8 7900 james CLERK 7698.0 1981-12-11 0:00 950 NaN 30
8 9 7521 ward SALESMAN 7698.0 1981-02-23 0:00 1250 500.0 30
9 10 7902 ford ANALYST 7566.0 1981-12-11 0:00 3000 NaN 20
10 11 7369 smith CLERK 7902.0 1980-12-09 0:00 800 NaN 20
11 12 7788 scott ANALYST 7566.0 1982-12-22 0:00 3000 NaN 20
12 13 7876 adams CLERK 7788.0 1983-01-15 0:00 1100 NaN 20
13 14 7934 miller CLERK 7782.0 1982-01-11 0:00 1300 NaN 10
In [37]:
emp[['ename']] = emp.ename.str.upper()
emp
Out[37]:
index empno ename job mgr hiredate sal comm deptno
0 1 7839 KING PRESIDENT NaN 1981-11-17 0:00 5000 NaN 10
1 2 7698 BLAKE MANAGER 7839.0 1981-05-01 0:00 2850 NaN 30
2 3 7782 CLARK MANAGER 7839.0 1981-05-09 0:00 2450 NaN 10
3 4 7566 JONES MANAGER 7839.0 1981-04-01 0:00 2975 NaN 20
4 5 7654 MARTIN SALESMAN 7698.0 1981-09-10 0:00 1250 1400.0 30
5 6 7499 ALLEN SALESMAN 7698.0 1981-02-11 0:00 1600 300.0 30
6 7 7844 TURNER SALESMAN 7698.0 1981-08-21 0:00 1500 0.0 30
7 8 7900 JAMES CLERK 7698.0 1981-12-11 0:00 950 NaN 30
8 9 7521 WARD SALESMAN 7698.0 1981-02-23 0:00 1250 500.0 30
9 10 7902 FORD ANALYST 7566.0 1981-12-11 0:00 3000 NaN 20
10 11 7369 SMITH CLERK 7902.0 1980-12-09 0:00 800 NaN 20
11 12 7788 SCOTT ANALYST 7566.0 1982-12-22 0:00 3000 NaN 20
12 13 7876 ADAMS CLERK 7788.0 1983-01-15 0:00 1100 NaN 20
13 14 7934 MILLER CLERK 7782.0 1982-01-11 0:00 1300 NaN 10
In [38]:
# Series로 출력
emp['ename']
Out[38]:
0       KING
1      BLAKE
2      CLARK
3      JONES
4     MARTIN
5      ALLEN
6     TURNER
7      JAMES
8       WARD
9       FORD
10     SMITH
11     SCOTT
12     ADAMS
13    MILLER
Name: ename, dtype: object
In [39]:
# DataFrame으로 출력
emp[['ename']]
Out[39]:
ename
0 KING
1 BLAKE
2 CLARK
3 JONES
4 MARTIN
5 ALLEN
6 TURNER
7 JAMES
8 WARD
9 FORD
10 SMITH
11 SCOTT
12 ADAMS
13 MILLER

합치기(concat,append)

In [103]:
new = [15, 7321, 'DEWY', 'MANAGER', np.NaN, '2020-07-16', 3000, np.nan, 50]

# index, columns는 반드시 리스트 형으로 넣어줘야함
# 참고 : https://freedata.tistory.com/53
new_df = pd.DataFrame(new,index=emp.columns,columns=['A']).T
new_df
Out[103]:
index empno ename job mgr hiredate sal comm deptno
A 15 7321 DEWY MANAGER NaN 2020-07-16 3000 NaN 50
In [104]:
# 데이터프레임을 데이터프레임에 넣으면 append나 concat이나 같은결과가 나온다.
emp.append(new_df,ignore_index=True)
Out[104]:
index empno ename job mgr hiredate sal comm deptno
0 1 7839 KING PRESIDENT NaN 1981-11-17 0:00 5000 NaN 10
1 2 7698 BLAKE MANAGER 7839.0 1981-05-01 0:00 2850 NaN 30
2 3 7782 CLARK MANAGER 7839.0 1981-05-09 0:00 2450 NaN 10
3 4 7566 JONES MANAGER 7839.0 1981-04-01 0:00 2975 NaN 20
4 5 7654 MARTIN SALESMAN 7698.0 1981-09-10 0:00 1250 1400.0 30
5 6 7499 ALLEN SALESMAN 7698.0 1981-02-11 0:00 1600 300.0 30
6 7 7844 TURNER SALESMAN 7698.0 1981-08-21 0:00 1500 0.0 30
7 8 7900 JAMES CLERK 7698.0 1981-12-11 0:00 950 NaN 30
8 9 7521 WARD SALESMAN 7698.0 1981-02-23 0:00 1250 500.0 30
9 10 7902 FORD ANALYST 7566.0 1981-12-11 0:00 3000 NaN 20
10 11 7369 SMITH CLERK 7902.0 1980-12-09 0:00 800 NaN 20
11 12 7788 SCOTT ANALYST 7566.0 1982-12-22 0:00 3000 NaN 20
12 13 7876 ADAMS CLERK 7788.0 1983-01-15 0:00 1100 NaN 20
13 14 7934 MILLER CLERK 7782.0 1982-01-11 0:00 1300 NaN 10
14 15 7321 DEWY MANAGER NaN 2020-07-16 3000 NaN 50
In [107]:
pd.concat([emp,new_df],axis=0,ignore_index=False)
Out[107]:
index empno ename job mgr hiredate sal comm deptno
0 1 7839 KING PRESIDENT NaN 1981-11-17 0:00 5000 NaN 10
1 2 7698 BLAKE MANAGER 7839.0 1981-05-01 0:00 2850 NaN 30
2 3 7782 CLARK MANAGER 7839.0 1981-05-09 0:00 2450 NaN 10
3 4 7566 JONES MANAGER 7839.0 1981-04-01 0:00 2975 NaN 20
4 5 7654 MARTIN SALESMAN 7698.0 1981-09-10 0:00 1250 1400.0 30
5 6 7499 ALLEN SALESMAN 7698.0 1981-02-11 0:00 1600 300.0 30
6 7 7844 TURNER SALESMAN 7698.0 1981-08-21 0:00 1500 0.0 30
7 8 7900 JAMES CLERK 7698.0 1981-12-11 0:00 950 NaN 30
8 9 7521 WARD SALESMAN 7698.0 1981-02-23 0:00 1250 500.0 30
9 10 7902 FORD ANALYST 7566.0 1981-12-11 0:00 3000 NaN 20
10 11 7369 SMITH CLERK 7902.0 1980-12-09 0:00 800 NaN 20
11 12 7788 SCOTT ANALYST 7566.0 1982-12-22 0:00 3000 NaN 20
12 13 7876 ADAMS CLERK 7788.0 1983-01-15 0:00 1100 NaN 20
13 14 7934 MILLER CLERK 7782.0 1982-01-11 0:00 1300 NaN 10
A 15 7321 DEWY MANAGER NaN 2020-07-16 3000 NaN 50

일반 리스트형을 append하면 결과값 이상하게 나옴 시리즈도 마찬가지...

In [91]:
new2 = pd.Series([15, 7321, 'DEWY', 'MANAGER', np.NaN, '2020-07-16', 3000, np.nan, 50])

emp.append(new2,ignore_index=True)
pd.concat([emp,new_df],axis=1)
Out[91]:
index empno ename job mgr hiredate sal comm deptno index empno ename job mgr hiredate sal comm deptno
0 1.0 7839.0 KING PRESIDENT NaN 1981-11-17 0:00 5000.0 NaN 10.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
1 2.0 7698.0 BLAKE MANAGER 7839.0 1981-05-01 0:00 2850.0 NaN 30.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
2 3.0 7782.0 CLARK MANAGER 7839.0 1981-05-09 0:00 2450.0 NaN 10.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
3 4.0 7566.0 JONES MANAGER 7839.0 1981-04-01 0:00 2975.0 NaN 20.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
4 5.0 7654.0 MARTIN SALESMAN 7698.0 1981-09-10 0:00 1250.0 1400.0 30.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
5 6.0 7499.0 ALLEN SALESMAN 7698.0 1981-02-11 0:00 1600.0 300.0 30.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
6 7.0 7844.0 TURNER SALESMAN 7698.0 1981-08-21 0:00 1500.0 0.0 30.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
7 8.0 7900.0 JAMES CLERK 7698.0 1981-12-11 0:00 950.0 NaN 30.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
8 9.0 7521.0 WARD SALESMAN 7698.0 1981-02-23 0:00 1250.0 500.0 30.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
9 10.0 7902.0 FORD ANALYST 7566.0 1981-12-11 0:00 3000.0 NaN 20.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
10 11.0 7369.0 SMITH CLERK 7902.0 1980-12-09 0:00 800.0 NaN 20.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
11 12.0 7788.0 SCOTT ANALYST 7566.0 1982-12-22 0:00 3000.0 NaN 20.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
12 13.0 7876.0 ADAMS CLERK 7788.0 1983-01-15 0:00 1100.0 NaN 20.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
13 14.0 7934.0 MILLER CLERK 7782.0 1982-01-11 0:00 1300.0 NaN 10.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
15 NaN NaN NaN NaN NaN NaN NaN NaN NaN 15 7321 DEWY MANAGER NaN 2020-07-16 3000 NaN 50

그룹화(groupby)

In [116]:
emp.groupby('job').sal.sum().reset_index()
Out[116]:
job sal
0 ANALYST 6000
1 CLERK 4150
2 MANAGER 8275
3 PRESIDENT 5000
4 SALESMAN 5600

함수 적용(apply)

In [165]:
dates = pd.date_range('20130101', periods=6)
df = pd.DataFrame(np.random.randn(6,4), index=dates, columns=list('ABCD'))
df
Out[165]:
A B C D
2013-01-01 0.181765 0.421731 -0.391242 -0.949458
2013-01-02 -1.293275 -0.690032 1.194403 -0.189533
2013-01-03 0.471382 1.351683 0.648124 -0.128018
2013-01-04 -0.748874 -0.648833 -0.393539 0.435896
2013-01-05 0.098486 -2.092289 1.048243 -1.068479
2013-01-06 -1.919552 1.020284 0.426926 -0.297642
In [166]:
df.apply(lambda x: (x-x.min()) / (x.max() - x.min()))
Out[166]:
A B C D
2013-01-01 0.878869 0.729977 0.001447 0.079117
2013-01-02 0.261938 0.407163 1.000000 0.584260
2013-01-03 1.000000 1.000000 0.655983 0.625151
2013-01-04 0.489632 0.419125 0.000000 1.000000
2013-01-05 0.844038 0.000000 0.907956 0.000000
2013-01-06 0.000000 0.903774 0.516685 0.512397

Join

In [132]:
emp2 = emp.append(new_df,ignore_index=True)
emp2
Out[132]:
index empno ename job mgr hiredate sal comm deptno
0 1 7839 KING PRESIDENT NaN 1981-11-17 0:00 5000 NaN 10
1 2 7698 BLAKE MANAGER 7839.0 1981-05-01 0:00 2850 NaN 30
2 3 7782 CLARK MANAGER 7839.0 1981-05-09 0:00 2450 NaN 10
3 4 7566 JONES MANAGER 7839.0 1981-04-01 0:00 2975 NaN 20
4 5 7654 MARTIN SALESMAN 7698.0 1981-09-10 0:00 1250 1400.0 30
5 6 7499 ALLEN SALESMAN 7698.0 1981-02-11 0:00 1600 300.0 30
6 7 7844 TURNER SALESMAN 7698.0 1981-08-21 0:00 1500 0.0 30
7 8 7900 JAMES CLERK 7698.0 1981-12-11 0:00 950 NaN 30
8 9 7521 WARD SALESMAN 7698.0 1981-02-23 0:00 1250 500.0 30
9 10 7902 FORD ANALYST 7566.0 1981-12-11 0:00 3000 NaN 20
10 11 7369 SMITH CLERK 7902.0 1980-12-09 0:00 800 NaN 20
11 12 7788 SCOTT ANALYST 7566.0 1982-12-22 0:00 3000 NaN 20
12 13 7876 ADAMS CLERK 7788.0 1983-01-15 0:00 1100 NaN 20
13 14 7934 MILLER CLERK 7782.0 1982-01-11 0:00 1300 NaN 10
14 15 7321 DEWY MANAGER NaN 2020-07-16 3000 NaN 50
In [126]:
dept = pd.read_csv("c:/data/dept.csv")
dept
Out[126]:
deptno dname loc
0 10 ACCOUNTING NEW YORK
1 20 RESEARCH DALLAS
2 30 SALES CHICAGO
3 40 OPERATIONS BOSTON
In [139]:
pd.merge(emp2,dept,on='deptno') # default : how = 'inner'
Out[139]:
index empno ename job mgr hiredate sal comm deptno dname loc
0 1 7839 KING PRESIDENT NaN 1981-11-17 0:00 5000 NaN 10 ACCOUNTING NEW YORK
1 3 7782 CLARK MANAGER 7839.0 1981-05-09 0:00 2450 NaN 10 ACCOUNTING NEW YORK
2 14 7934 MILLER CLERK 7782.0 1982-01-11 0:00 1300 NaN 10 ACCOUNTING NEW YORK
3 2 7698 BLAKE MANAGER 7839.0 1981-05-01 0:00 2850 NaN 30 SALES CHICAGO
4 5 7654 MARTIN SALESMAN 7698.0 1981-09-10 0:00 1250 1400.0 30 SALES CHICAGO
5 6 7499 ALLEN SALESMAN 7698.0 1981-02-11 0:00 1600 300.0 30 SALES CHICAGO
6 7 7844 TURNER SALESMAN 7698.0 1981-08-21 0:00 1500 0.0 30 SALES CHICAGO
7 8 7900 JAMES CLERK 7698.0 1981-12-11 0:00 950 NaN 30 SALES CHICAGO
8 9 7521 WARD SALESMAN 7698.0 1981-02-23 0:00 1250 500.0 30 SALES CHICAGO
9 4 7566 JONES MANAGER 7839.0 1981-04-01 0:00 2975 NaN 20 RESEARCH DALLAS
10 10 7902 FORD ANALYST 7566.0 1981-12-11 0:00 3000 NaN 20 RESEARCH DALLAS
11 11 7369 SMITH CLERK 7902.0 1980-12-09 0:00 800 NaN 20 RESEARCH DALLAS
12 12 7788 SCOTT ANALYST 7566.0 1982-12-22 0:00 3000 NaN 20 RESEARCH DALLAS
13 13 7876 ADAMS CLERK 7788.0 1983-01-15 0:00 1100 NaN 20 RESEARCH DALLAS
In [141]:
pd.merge(emp2,dept,on='deptno',how='left')
Out[141]:
index empno ename job mgr hiredate sal comm deptno dname loc
0 1 7839 KING PRESIDENT NaN 1981-11-17 0:00 5000 NaN 10 ACCOUNTING NEW YORK
1 2 7698 BLAKE MANAGER 7839.0 1981-05-01 0:00 2850 NaN 30 SALES CHICAGO
2 3 7782 CLARK MANAGER 7839.0 1981-05-09 0:00 2450 NaN 10 ACCOUNTING NEW YORK
3 4 7566 JONES MANAGER 7839.0 1981-04-01 0:00 2975 NaN 20 RESEARCH DALLAS
4 5 7654 MARTIN SALESMAN 7698.0 1981-09-10 0:00 1250 1400.0 30 SALES CHICAGO
5 6 7499 ALLEN SALESMAN 7698.0 1981-02-11 0:00 1600 300.0 30 SALES CHICAGO
6 7 7844 TURNER SALESMAN 7698.0 1981-08-21 0:00 1500 0.0 30 SALES CHICAGO
7 8 7900 JAMES CLERK 7698.0 1981-12-11 0:00 950 NaN 30 SALES CHICAGO
8 9 7521 WARD SALESMAN 7698.0 1981-02-23 0:00 1250 500.0 30 SALES CHICAGO
9 10 7902 FORD ANALYST 7566.0 1981-12-11 0:00 3000 NaN 20 RESEARCH DALLAS
10 11 7369 SMITH CLERK 7902.0 1980-12-09 0:00 800 NaN 20 RESEARCH DALLAS
11 12 7788 SCOTT ANALYST 7566.0 1982-12-22 0:00 3000 NaN 20 RESEARCH DALLAS
12 13 7876 ADAMS CLERK 7788.0 1983-01-15 0:00 1100 NaN 20 RESEARCH DALLAS
13 14 7934 MILLER CLERK 7782.0 1982-01-11 0:00 1300 NaN 10 ACCOUNTING NEW YORK
14 15 7321 DEWY MANAGER NaN 2020-07-16 3000 NaN 50 NaN NaN
In [142]:
pd.merge(emp2,dept,on='deptno',how='right')
Out[142]:
index empno ename job mgr hiredate sal comm deptno dname loc
0 1 7839 KING PRESIDENT NaN 1981-11-17 0:00 5000 NaN 10.0 ACCOUNTING NEW YORK
1 3 7782 CLARK MANAGER 7839.0 1981-05-09 0:00 2450 NaN 10.0 ACCOUNTING NEW YORK
2 14 7934 MILLER CLERK 7782.0 1982-01-11 0:00 1300 NaN 10.0 ACCOUNTING NEW YORK
3 2 7698 BLAKE MANAGER 7839.0 1981-05-01 0:00 2850 NaN 30.0 SALES CHICAGO
4 5 7654 MARTIN SALESMAN 7698.0 1981-09-10 0:00 1250 1400.0 30.0 SALES CHICAGO
5 6 7499 ALLEN SALESMAN 7698.0 1981-02-11 0:00 1600 300.0 30.0 SALES CHICAGO
6 7 7844 TURNER SALESMAN 7698.0 1981-08-21 0:00 1500 0.0 30.0 SALES CHICAGO
7 8 7900 JAMES CLERK 7698.0 1981-12-11 0:00 950 NaN 30.0 SALES CHICAGO
8 9 7521 WARD SALESMAN 7698.0 1981-02-23 0:00 1250 500.0 30.0 SALES CHICAGO
9 4 7566 JONES MANAGER 7839.0 1981-04-01 0:00 2975 NaN 20.0 RESEARCH DALLAS
10 10 7902 FORD ANALYST 7566.0 1981-12-11 0:00 3000 NaN 20.0 RESEARCH DALLAS
11 11 7369 SMITH CLERK 7902.0 1980-12-09 0:00 800 NaN 20.0 RESEARCH DALLAS
12 12 7788 SCOTT ANALYST 7566.0 1982-12-22 0:00 3000 NaN 20.0 RESEARCH DALLAS
13 13 7876 ADAMS CLERK 7788.0 1983-01-15 0:00 1100 NaN 20.0 RESEARCH DALLAS
14 NaN NaN NaN NaN NaN NaN NaN NaN 40.0 OPERATIONS BOSTON

압축(stack)

In [152]:
tuples = list(zip(*[['bar', 'bar', 'baz', 'baz',
                     'foo', 'foo', 'qux', 'qux'],
                    ['one', 'two', 'one', 'two',
                     'one', 'two', 'one', 'two']]))
index = pd.MultiIndex.from_tuples(tuples, names=['first', 'second'])
df = pd.DataFrame(np.random.randn(8, 2), index=index, columns=['A', 'B'])
df2 = df[:4]
df2
Out[152]:
A B
first second
bar one -0.298864 0.247955
two -0.045396 0.610319
baz one -0.681461 -0.217126
two 0.706999 0.656189
In [155]:
stacked = df2.stack()
stacked
Out[155]:
first  second   
bar    one     A   -0.298864
               B    0.247955
       two     A   -0.045396
               B    0.610319
baz    one     A   -0.681461
               B   -0.217126
       two     A    0.706999
               B    0.656189
dtype: float64
In [156]:
stacked.unstack()
Out[156]:
A B
first second
bar one -0.298864 0.247955
two -0.045396 0.610319
baz one -0.681461 -0.217126
two 0.706999 0.656189

+ Recent posts