글쓴이 보관물: litcoder

한글 텍스트 추출을 위한 Python PDF module

이 내용은 2020년 3월에 작성된 것으로 참조하는 시점에 따라 변경된 사항들이 있을 수도 있습니다.

PyPDF2

PyPDF2는 PDF file의 metadata 정보를 가져오거나 페이지 단위로 나누거나 합치는 등의 여러가지 편리한 기능들을 제공한다. 하지만 한글을 제대로 추출하지 못하는 문제가 있어서(한글 뿐 아니라 CJK 모두 라고 함) 목적에는 적합하지 않았다.

PDFMiner

한글 처리는 문제 없다. 그런데 페이지 단위로 나누어서 처리하는 것을 따로 지원하지 않아서 원하는 페이지에 접근하려면 순차적으로 처음부터 해당 페이지를 찾아가는 trick을 사용해야 하는데, 이 코드로 순차적 접근을 하면 시간 복잡도가 O(N^2)가 되어 파일의 크기가 조금만 커도 성능이 매우 떨어진다.

Tika

많은 곳에서 쓰이는 꽤나 유명한 프로젝트인데 Python module로도 proting 되어 있다(tika-python). 한글 추출에는 문제가 없고, 이 모듈 자체에서는 페이지 단위의 텍스트 추출을 지원하지 않으나, 그대신 PDF를 XML로 추출한 다음에 BeautifulSoupe로 <div page=””> 태그를 찾아 페이지 단위로 접근하는 신박한 트릭이 있다(StackOverflow). 나는 BeautifulSoup의 paser로 lxml을 사용했다.

주의. 명시적으로 표시 되지는 않지만 JRE(Java Runtime Environment)에 의존하므로 동작시 오류가 발생하면 JRE가 제대로 설치 되어 있고 접근 가능한지 확인해 볼 것. Ubuntu 18.04 default-jre package (OpenJDK 11)로 동작 확인.

결론

PDF 문서 자체에 대한 합치기/나누기/정보 가져오기 등은 PyPDF2가 무척 편하다. 한글 텍스트 추출을 위해서는 Tika, 페이지 단위 접근이 필요하다면 Tika + BS를 고려해 볼 만하다. PDFMiner는 뭐랄까.. 쫌 별로..

[Tip] Emac에서 pdb 사용할 때 UnicodeEncodeError

*** UnicodeEncodeError: ‘ascii’ codec can’t encode characters in position 168-169: ordinal not in range(128)

Emacs에서 shell을 열고 pdb를 실행 할 때 표시하고자 하는 문자열이 ASCII가 아니라면 발생 할 수 있는 문제인데 emacs의 초기화 파일에 unicode locale 설정을 해주는 것으로 해결할 수 있다. 인터넷 문서들 중에는 LANG, LC_LANG, LC_CTYPE 모두를 설정해 주어야 한다는 내용도 있었으나, 내 terminal에서 LC_CTYPE만 설정해서도 잘 동작되고 있으므로 이를 따라 LC_CTYPE만 UTF-8으로 다음과 같이 설정해 주었다.

Init file을 reload하거나 emac를 재 실행해서 ASCII외의 문자들이 잘 표시 되는지 확인해 본다.

그리고 GitHub repository에 업뎃. 🙂

[Tip] Mac OSX에서 시작 프로그램 삭제

Mac OSX에서 로그인시 자동으로 시작되는 프로그램을 삭제 하는 방법들은 대략 다음과 같다.

  1. 태스크 바에서 오른 클릭 후 ‘로그인 시 열기’ 해제
  2. 설정 -> 사용자 및 그룹 -> 로그인 항목에서 삭제
  3. /Users/<사용자계정>/Library/LaunchAgents/에서 unload
  4. /Library/LaunchAgents/에서 unload

사용자 계정에 등록된 로그인 항목 unload (위의 3번)

위의 1번과 2번 방법은 인터넷에 많이 있으니, 테스크바에도 등록되어 있지 않고 설정의 로그인 항목에서도 프로그램이 보이지 않는 경우, 사용자 디렉토리 내의 LaunchAgents에서 찾아 삭제를 시도해 볼 수 있다. 다음은 CrashPlan이라는 프로그램을 사용자 계정 시작 항목에서 삭제 하는 예시이다.

시스템 전체로 등록된 로그인 항목 unload (위의 4번)

프로그램에 따라 사용자 계정별이 아닌 시스템 전체의 시작 프로그램으로 등록된 경우도 있다. 이 경우 /Library/LaunchAgents/에서 찾아 삭제를 시도할 수 있다. 다음은 Cisco AnyConnect VPN 프로그램을 시작 항목에서 삭제하는 예시이다.

Back propgation에서의 전치행렬(transpose matrix) – 2편

1편이 너무 길어 져서 \frac{\partial L}{\partial W} = X^T \cdot \frac{\partial L}{\partial Y}에 대한 유도는 여기로 나누었다.

이제 \frac{\partial Y}{\partial W}를 보면, 얘들도 모두 matrix이니 \frac {\partial Y}{\partial W}는 다음과 같이 생겼다.

\frac{\partial Y}{\partial W} = \begin{bmatrix}\frac{\partial Y}{\partial w_{1,1}} & \frac{\partial Y}{\partial w_{1,2}} & \frac{\partial Y}{\partial w_{1,3}} \\\\ \frac{\partial Y}{\partial w_{2,1}} & \frac{\partial Y}{\partial w_{2,2}} & \frac{\partial Y}{\partial w_{2,3}} \end{bmatrix}

첫번째 원소인 \frac{\partial Y}{\partial w_{1,1}}을 구하기 위해 이전 처럼, W에 대해 Y로 편미분하면 다음과 같이된다.

\frac{\partial Y}{\partial w_{1,1}}=\begin{bmatrix}x_{1,1} & 0 & 0 \\\\ x_{2,1} & 0 & 0\end{bmatrix} \\\\ \frac{\partial Y}{\partial w_{1,2}}=\begin{bmatrix}0 & x_{1,1} & 0 \\\\ 0 & x_{2,1} & 0\end{bmatrix} \\\\ \frac{\partial Y}{\partial w_{1,3}}=\begin{bmatrix}0 & 0 & x_{1,1} \\\\ 0 & 0 & x_{2,1}\end{bmatrix} \\\\ \frac{\partial Y}{\partial w_{2,1}}=\begin{bmatrix}x_{1,2} & 0 & 0 \\\\ x_{2,2} & 0 & 0\end{bmatrix} \\\\ \frac{\partial Y}{\partial w_{2,2}}=\begin{bmatrix}0 & x_{1,2} & 0 \\\\ 0 & x_{2,2} & 0\end{bmatrix} \\\\ \frac{\partial Y}{\partial w_{2,3}}=\begin{bmatrix}0 & 0 & x_{1,2} \\\\ 0 & 0 & x_{2,2}\end{bmatrix}

Matrix W의 각 원소들 역시 scalar이므로 1편에서 X의 경우 처럼, 다음과 같이 나타낼 수 있다.

\frac{\partial L}{\partial w_{1,1}} = \sum_{i=1}{N} \sum_{j=1}{M}\frac{\partial L}{\partial y_{i,j}} \cdot \frac{\partial y_{i,j}}{\partial w_{1,1}}

\frac{\partial L}{\partial w_{1,1}} = (\frac{\partial L}{\partial y_{1,1}} \times x_{1,1}) + (\frac{\partial L}{\partial y_{2,1}} \times x_{2,1}) \\\\ \frac{\partial L}{\partial w_{1,2}} = (\frac{\partial L}{\partial y_{1,2}} \times x_{1,1}) + (\frac{\partial L}{\partial y_{2,2}} \times x_{2,1}) \\\\ \frac{\partial L}{\partial w_{1,3}} = (\frac{\partial L}{\partial y_{1,3}} \times x_{1,1}) + (\frac{\partial L}{\partial y_{2,3}} \times x_{2,1}) \\\\ \frac{\partial L}{\partial w_{2,1}} = (\frac{\partial L}{\partial y_{1,1}} \times x_{1,2}) + (\frac{\partial L}{\partial y_{2,1}} \times x_{2,2}) \\\\ \frac{\partial L}{\partial w_{2,2}} = (\frac{\partial L}{\partial y_{1,2}} \times x_{1,2}) + (\frac{\partial L}{\partial y_{2,2}} \times x_{2,2}) \\\\ \frac{\partial L}{\partial w_{2,3}} = (\frac{\partial L}{\partial y_{1,3}} \times x_{1,2}) + (\frac{\partial L}{\partial y_{2,3}} \times x_{2,2})

이것을 2X3인 matrix로 나타내면

\frac{\partial L}{\partial W} = \begin{bmatrix}(\frac{\partial L}{\partial y_{1,1}} \times x_{1,1}) + (\frac{\partial L}{\partial y_{2,1}} \times x_{2,1}) & (\frac{\partial L}{\partial y_{1,2}} \times x_{1,1}) + (\frac{\partial L}{\partial y_{2,2}} \times x_{2,1}) & (\frac{\partial L}{\partial y_{1,3}} \times x_{1,1}) + (\frac{\partial L}{\partial y_{2,3}} \times x_{2,1}) \\\\ (\frac{\partial L}{\partial y_{1,1}} \times x_{1,2}) + (\frac{\partial L}{\partial y_{2,1}} \times x_{2,2}) & (\frac{\partial L}{\partial y_{1,2}} \times x_{1,2}) + (\frac{\partial L}{\partial y_{2,2}} \times x_{2,2}) & (\frac{\partial L}{\partial y_{1,3}} \times x_{1,2}) + (\frac{\partial L}{\partial y_{2,3}} \times x_{2,2}) \end{bmatrix}

Matrix X와 W원소의 위치를 바꿔서 나타내면

\frac{\partial L}{\partial W} = \begin{bmatrix}(x_{1,1} \times \frac{\partial L}{\partial y_{1,1}}) + (x_{2,1} \times \frac{\partial L}{\partial y_{2,1}}) & (x_{1,1} \times \frac{\partial L}{\partial y_{1,2}}) + (x_{2,1} \times \frac{\partial L}{\partial y_{2,2}}) & (x_{2,1} \times \frac{\partial L}{\partial y_{1,3}}) + (x_{1,1} \times \frac{\partial L}{\partial y_{2,3}})  \\\\ (x_{1,2} \times \frac{\partial L}{\partial y_{1,1}}) + (x_{2,2} \times \frac{\partial L}{\partial y_{2,1}}) & (x_{1,2} \times \frac{\partial L}{\partial y_{1,2}}) + (x_{2,2} \times \frac{\partial L}{\partial y_{2,2}}) & (x_{1,2} \times \frac{\partial L}{\partial y_{1,3}}) + (x_{2,2} \times \frac{\partial L}{\partial y_{2,3}}) \end{bmatrix}

Matrix X와 Y로 구분하면

\frac{\partial L}{\partial W} = \begin{bmatrix}x_{1,1} & x_{2,1} \\\\ x_{1,2} & x_{2,2}\end{bmatrix} \cdot \begin{bmatrix}\frac{\partial L}{\partial y_{1,1}}  & \frac{\partial L}{\partial y_{1,2}} & \frac{\partial L}{\partial y_{1,3}} \\\\ \frac{\partial L}{\partial y_{2,1}} & \frac{\partial L}{\partial y_{2,2}} & \frac{\partial L}{\partial y_{2,3}}\end{bmatrix} = X^T \cdot \frac{\partial L}{\partial Y}이 성립한다.

Back propgation에서의 전치행렬(transpose matrix) – 1편

실제로 전개해보면 다음 식이 도출됩니다([식 5.13]으로 이끄는 과정은 생략합니다).

– p172, 5.6.1 Affine 계층, 밑바닥부터 시작하는 딥러닝

아니! 그걸 생략하면 어떡해요!!

“밑바닥부터 시작하는 딥러닝”을 읽으면서 딥러닝의 개념을 잡는데 많은 도움을 받고 있지만 굳이 단점을 들자면 주요한 공식 들에 대해 설명하지 않고 그냥 넘어 가버리는 경우가 가끔 있다. 위에서 말하는 [식 5.13]은 back propagataion에서 입력에 대한 loss function의 영향과 weight에 대한 loss function의 영향을 계산하는 다음 식을 의미한다.

\frac{\partial L}{\partial X} = \frac{\partial L}{\partial Y} \cdot W^T \\\\ \frac{\partial L}{\partial W} = X^Y \cdot \frac{\partial L}{\partial Y}

이 식이 도대체 어떻게 유도된 것인지 이리 저리 찾다가 마침 이 부분을 자세히 설명해 주고 있는 미국 어느 대학(!)의 훌륭한 문서(Backpropagation for a Linear Layer, Justin Johnson, April 19, 2017)를 발견했다. 이 포스팅은 해당 문서에 대한 나름의 이해를 정리한 것이다.

밑밥 깔기

Matrix인 입력 X, Weight W가 있다고 할 때, 이 둘의 dot product인 Y는 다음과 같은 모습이다.

X = \begin{bmatrix}x_{1,1} & x_{1,2}\\x_{2,1} & x_{2,2}\end{bmatrix} W = \begin{bmatrix}w_{1,1} & w_{1,2} & w_{1,3}\\w_{2,1} & w_{2,2} & w_{2,3}\end{bmatrix} Y = X \cdot W = \begin{bmatrix}x_{1,1}w_{1,1} + x_{1,2}w_{2,1} & x_{1,1}w_{1,2} + x_{1,2}w_{2,2} & x_{1,1} w_{1,3} + x_{1,2}w_{2,3} \\\\ x_{2,1}w_{1,1} + x_{2,2}w_{2,1} & x_{2,1}w_{1,2} + x_{2,2}w_{2,2} & x_{2,1}w_{1,3} + x_{2,2}w_{2,3}\end{bmatrix}

Back propagation을 통해 최종으로 구하고자 하는 것은 입력의 변화에 따른 loss function의 변화량 \frac{\partial L}{\partial X}과 Weight 변화에 따른 loss function의 변화량 \frac{\partial L}{\partial W}이다. 이것과 관련해 연쇄 법칙(chain rule)에 따라 이전 layer에서 전달 받은 Y = X \cdot W의 변화에 따른 loss function의 변화량인 \frac{\partial L}{\partial Y}를 고려하면 다음이 성립한다.

\frac{\partial L}{\partial X} = \frac{\partial L}{\partial Y} \cdot \frac{\partial Y}{\partial X} \\\\ \frac{\partial L}{\partial W} = \frac{\partial L}{\partial Y} \cdot \frac{\partial Y}{\partial W}

Y의 변화에 따른 Loss function의 변화 \frac{\partial L}{\partial Y}

여기에서 \partial L은 scalar 값 이고 Y는 matrix이므로 \frac{\partial L}{\partial Y}의 모습은 다음과 같다.

\begin{bmatrix}\frac{\partial L}{\partial (x_{1,1}w_{1,1} + x_{1,2}w_{2,1})} & \frac {\partial L}{\partial (x_{1,1}w_{1,2} + x_{1,2}w_{2,2})} & \frac {\partial L}{\partial (x_{1,1} w_{1,3} + x_{1,2}w_{2,3})} \\\\ \frac{\partial L}{\partial (x_{2,1}w_{1,1} + x_{2,2}w_{2,1})} & \frac{\partial L}{\partial (x_{2,1}w_{1,2} + x_{2,2}w_{2,2})} & \frac{\partial L}{\partial (x_{2,1}w_{1,3} + x_{2,2}w_{2,3})}\end{bmatrix}

복잡하니까 조금 간단히 다음과 같이 인덱스로 나타내자.

\frac{\partial L}{\partial Y} = \begin{bmatrix}\frac{\partial L}{\partial y_{1,1}} & \frac {\partial L}{\partial y_{1,2}} & \frac {\partial L}{\partial y_{1,3}} \\\\ \frac{\partial L}{\partial y_{2,1}} & \frac{\partial L}{\partial y_{2,2}} & \frac{\partial L}{\partial y_{2,3}}\end{bmatrix}

이제, \frac{\partial Y}{\partial X}\frac{\partial Y}{\partial W}가 남았다.

행렬 X의 원소들에 대한 행렬 Y의 편미분 \frac{\partial Y}{\partial X}

먼저 \frac{\partial Y}{\partial X}를 보면, X와 Y 모두 matrix이니 \frac {\partial Y}{\partial X}는 다음과 같이 생겼다.

\frac{\partial Y}{\partial X} = \begin{bmatrix}\frac{\partial Y}{\partial x_{1,1}} & \frac{\partial Y}{\partial x_{1,2}} & \frac{\partial Y}{\partial x_{1,3}} \\\\ \frac{\partial Y}{\partial x_{2,1}} & \frac{\partial Y}{\partial x_{2,2}} & \frac{\partial Y}{\partial x_{2,3}} \end{bmatrix}

각 원소들은 scalar 값인데 그 중 첫번째 원소인 \frac{\partial Y}{\partial x_{1,1}}를 구하기 위해 Y의 원소들을 x_{1,1}로 편미분 하면 다음과 같이 된다.

\frac{\partial Y}{\partial x_{1,1}}=\begin{bmatrix}w_{1,1} & w_{1,2} & w_{1,3} \\\\ 0 & 0 & 0 \end{bmatrix}

응? 갑자기 이건 뭐냐!

예를 들어 y_{1,1}에 있는 x_{1,1}w_{1,1} + x_{1,2}w_{2,1}x_{1,1}로 편미분하면 w_{1,1}가 되고, x_{1,1}w_{1,2} + x_{1,2}w_{2,2}에 대해서도 같은 방식으로 하면 w_{1,2}가 되는 식으로 Y의 모든 6개의 원소에 적용한 것이다. 이런 짓을 matrix X의 모든 원소인 x_{1, 2}, x_{2,1}, x_{2,2}에 대해서도 모두 구하면 다음과 같이 된다.

\frac{\partial Y}{\partial x_{1,1}}=\begin{bmatrix}w_{1,1} & w_{1,2} & w_{1,3} \\\\ 0 & 0 & 0\end{bmatrix} \\\\ \frac{\partial Y}{\partial x_{1,2}}=\begin{bmatrix}w_{2,1} & w_{2,2} & w_{2,3} \\\\ 0 & 0 & 0 \end{bmatrix} \\\\ \frac{\partial Y}{\partial x_{2,1}}=\begin{bmatrix} 0 & 0 & 0 \\\\ w_{1,1} & w_{1,2} & w_{1,3} \end{bmatrix} \\\\ \frac{\partial Y}{\partial x_{2,2}}=\begin{bmatrix} 0 & 0 & 0 \\\\ w_{2,1} & w_{2,2} & w_{2,3} \end{bmatrix}

행렬 X에 대한 scalar L의 편미분 \frac {\partial L}{\partial X}

\frac{\partial Y}{\partial x_{1,1}}은 matrix X를 구성하는 element인 scalar값이다. 위에서 말한것 처럼 연쇄법칙(Chain rule)에 의해 Y의 모든 원소들에 대하여 다음과 같이 나타낼 수 있다.

\frac{\partial L}{\partial x_{1,1}} = \sum_{i=1}^{N} \sum_{j=1}^{M} \frac{\partial L}{\partial y_{i,j}} \cdot \frac{\partial y_{i,j}}{\partial x_{1,1}}

Matrix X의 첫번째 원소는 {\partial L}를 matrix Y의 각 원소들로 나눈 값들에 Y의 각원소들을 X의 첫번째 원소로 편미분한 값들을 곱한 것을 모두 더한 것이다. 말이 드럽게 복잡해 보이지만, 예를들어, 첫번째 원소인 \frac{\partial L}{\partial x_{1,1}}의 값이 다음과 같이 계산된다는 뜻이다.

\frac{\partial L}{\partial x_{1,1}} = (\frac{\partial L}{\partial y_{1,1}} \times \frac{\partial y_{1,1}}{\partial x_{1,1}}) + (\frac{\partial L}{\partial y_{1,2}} \times \frac{\partial y_{1,2}}{\partial x_{1,1}}) + (\frac{\partial L}{\partial y_{1,3}} \times \frac{\partial y_{1,3}}{\partial x_{1,1}}) + (\frac{\partial L}{\partial y_{2,1}} \times \frac{\partial y_{2,1}}{\partial x_{1,1}}) + (\frac{\partial L}{\partial y_{2,2}} \times \frac{\partial y_{2,2}}{\partial x_{1,1}}) + (\frac{\partial L}{\partial y_{2,3}} \times \frac{\partial y_{2,3}}{\partial x_{1,1}})

이것도 뭐 딱히 깨끗해 보이진 않지만… 여튼, matrix Y의 각원소들에 대해 x_{1,1}로 편미분한 결과를 위 식에 적용해 보면 다음과 같이 된다.

\frac{\partial L}{\partial x_{1,1}} = (\frac{\partial L}{\partial y_{1,1}} \times w_{1,1}) + (\frac{\partial L}{\partial y_{1,2}} \times w_{1,2}) + (\frac{\partial L}{\partial y_{1,3}} \times w_{1,3}) + (\frac{\partial L}{\partial y_{2,1}} \times 0) + (\frac{\partial L}{\partial y_{2,2}} \times 0) + (\frac{\partial L}{\partial y_{2,3}} \times 0) \\\\ = (\frac{\partial L}{\partial y_{1,1}} \times w_{1,1}) + (\frac{\partial L}{\partial y_{1,2}} \times w_{1,2}) + (\frac{\partial L}{\partial y_{1,3}} \times w_{1,3})

같은 방법을 \frac{\partial L}{\partial X}의 모든 원소들에 적용하면

\frac{\partial L}{\partial x_{1,1}} = (\frac{\partial L}{\partial y_{1,1}} \times w_{1,1}) + (\frac{\partial L}{\partial y_{1,2}} \times w_{1,2}) + (\frac{\partial L}{\partial y_{1,3}} \times w_{1,3}) \\\\ \frac{\partial L}{\partial x_{1,2}} = (\frac{\partial L}{\partial y_{1,1}} \times w_{2,1}) + (\frac{\partial L}{\partial y_{1,2}} \times w_{2,2}) + (\frac{\partial L}{\partial y_{1,3}} \times w_{2,3}) \\\\ \frac{\partial L}{\partial x_{2,1}} = (\frac{\partial L}{\partial y_{2,1}} \times w_{1,1}) + (\frac{\partial L}{\partial y_{3,2}} \times w_{1,2}) + (\frac{\partial L}{\partial y_{3,3}} \times w_{1,3}) \\\\ \frac{\partial L}{\partial x_{2,2}} = (\frac{\partial L}{\partial y_{2,1}} \times w_{2,1}) + (\frac{\partial L}{\partial y_{3,2}} \times w_{2,2}) + (\frac{\partial L}{\partial y_{3,3}} \times w_{2,3})

이것을 matrix의 형태로 나타내면

\frac{\partial L}{\partial X}=\begin{bmatrix}(\frac{\partial L}{\partial y_{1,1}} \times w_{1,1}) + (\frac{\partial L}{\partial y_{1,2}} \times w_{1,2}) + (\frac{\partial L}{\partial y_{1,3}} \times w_{1,3}) & (\frac{\partial L}{\partial y_{1,1}} \times w_{2,1}) + (\frac{\partial L}{\partial y_{1,2}} \times w_{2,2}) + (\frac{\partial L}{\partial y_{1,3}} \times w_{2,3}) \\\\ (\frac{\partial L}{\partial y_{2,1}} \times w_{1,1}) + (\frac{\partial L}{\partial y_{3,2}} \times w_{1,2}) + (\frac{\partial L}{\partial y_{3,3}} \times w_{1,3}) & (\frac{\partial L}{\partial y_{2,1}} \times w_{2,1}) + (\frac{\partial L}{\partial y_{3,2}} \times w_{2,2}) + (\frac{\partial L}{\partial y_{3,3}} \times w_{2,3})\end{bmatrix}

Matrix Y와 W를 구분해 보면

\frac{\partial L}{\partial X}=\begin{bmatrix}\frac{\partial L}{\partial y_{1,1}} & \frac{\partial L}{\partial y_{1,2}} & \frac{\partial L}{\partial y_{1,3}} \\\\ \frac{\partial L}{\partial y_{2,1}} & \frac{\partial L}{\partial y_{2,2}} & \frac{\partial L}{\partial y_{2,3}}\end{bmatrix} \cdot \begin{bmatrix} w_{1,1} & w_{2,1} \\\\ w_{1,2} & w_{2,2} \\\\ w_{1,3} & w_{2,3}\end{bmatrix}

Weight matrix W의 전치행렬(transpose matrix)를 곱하는 것이 되므로,

\frac{\partial Y}{\partial X}=\frac{\partial L}{\partial Y} \cdot W^T이 성립한다.

Weight에 대한 loss function의 변화인 \frac{\partial L}{\partial W} = X^T \cdot \frac{\partial L}{\partial Y}도 기본적으로 같은 방법으로 유도 되는데 너무 길어져서 2편에서 간단히 다루도록 한다.