번역서 출간 여부를 쉽게 검색하기

이코노미스트지 선정 도서[1]나 개인적으로 관심있는 책의 번역서가 출간됐는지를 자주 검색하는데, 원서 제목을 키워드로 매번 검색하는게 상당히 귀찮다. 어떤 사람이 각종 서적 관련 api 제공을 비교하는 글[2]을 봤는데, 나도 해 볼 수 없을까 싶어 궁리해 봤다. ㅎ

개인적으로는 알라딘을 많이 사용하므로 알라딘 api를 이용하였다. 다른 api는 어떻게 이용하는지 모른다.

알라딘을 이용하려면 알라딘 ttb가 있어야 하는데[3], 본인의 경우는 옛날에 만들어 놓은 게 있어서 사용이 간단했다. 하루 5천번 사용 제한이 있다고 하던데[3], 본인은 5천번은 커녕 백 번도 이용할 일이 없으므로 충분하다.

아쉽게도 요즘 대세인 파이썬[4]은 할 줄 모르고, 본인이 쓸 줄 아는 게 perl 밖에 없으므로, 개인적으로 쓰는 웹서버[5]에 아래 스크립트를 사용하였다. 물론 LWP::Simple이 있어야 한다. 별도로 준비된 검색 키워드가 저장된 텍스트 파일을 읽어서, 그 키워드 목록을 검색해 주는 코드다. 키워드 파일 내에서 샾 기호(#) 이후로는 무시하므로 주석으로 사용할 수 있다.

#!/usr/bin/perl

use LWP::Simple;

open(FP, "검색 키워드 파일 절대 경로");
 @line = <FP>;
 $lines = @line;
close(FP);

print <<HTML_HEAD;
Content-type: text/html

<html><body>
HTML_HEAD

 while($lines>0)
 {
  if($line[--$lines] ne "\n")
  {
    @comment_sp = split(/\s*#|\n/, $line[$lines] );     #키워드 파일에서 샾 이후로 무시함.
    $temp_xml = get( "http://www.aladin.co.kr/ttb/api/ItemSearch.aspx?ttbkey=(본인의 ttb)&QueryType=Keyword&MaxResults=3&output=xml&Query=" . $comment_sp[0]);

    print "<br>$line[$lines] : ";

    if ( $temp_xml ) {
        @all=split("\n", $temp_xml);
        $num=@all;
        for($i=0;$i<$num;$i++)
        {
            if($all[$i] =~ m/<totalResults>/)
            {
                $all[$i] =~ s/\s*<\/?totalResults>\s*//g;

                if( $all[$i] > 0)
                {
                   print "<font color=red>$all[$i]</font>";
                   print "<dd>click : <a href=\"http://www.aladin.co.kr/ttb/api/ItemSearch.aspx?ttbkey=(본인의 ttb)&QueryType=Keyword&MaxResults=3&output=xml&Query=";
                   print "$line[$lines]\">$line[$lines]</a></dd>\n";
                }
                else
                {
                   print $all[$i] . "\n";
                }
            }
        }
    }
    else
    {
        print "404 not found";
    }
  }
 }

print "\n</body></html>";

본인은 프로그래머가 아니라서, 변수 이름이라든지 등등 여러가지로 코드가 더럽긴 하다. 욕하지 마시라-_- 더 아름답게 xml을 다루는 방법이 틀림없이 있겠지만, 좀 검색해보니 생각하기 귀찮아서, 그냥 regex로 막무가내로 프린트 했다.

여하간 이걸 이용하면 지정된 여러가지 책 제목들의 신간이 출간되었는지 한 방에 확인할 수 있다.

근데 이 코드의 문제점은 루프가 다 돌 때까지 결과가 출력되지 않는다는 점이 있다. 아마 Suffering from Buffering이라 불리는 현상[6] 같은데, 본인은 해결방법을 모르겠다. 이걸 해결해보려고 여러가지 삽질을 했는데, 결과가 신통치 않았다. 뭐 본인의 경우 키워드가 20여개 미만이라서 20초 정도 기다리면 해결되니까 그냥 기다리는 수 밖에 없을 듯 하다. ㅎㅎㅎ

뭐 이런 식으로 오렌지 파이[7]를 활용하는 방법도 있다는 이야기를 하고 싶었음. 근데 요새 술먹고 자꾸 글 쓰니 쓸데없는 이야기가 많아 지는 듯 하다. ㅎ

.


[1] 내 백과사전 2019 이코노미스트지 선정 올해의 책 2019년 12월 8일
[2] 국내 도서 검색 OPEN API 비교 (anpigon.github.io)
[3] OpenAPI 안내 (blog.aladin.co.kr)
[4] 이코노미스트 Python has brought computer programming to a vast new audience Jul 19th 2018
[5] 내 백과사전 라즈베리 파이를 어떻게 활용하십니까? 2019년 6월 26일
[6] Suffering from Buffering? (perl.plover.com)
[7] 내 백과사전 오렌지 파이 제로 wifi 연결하기 2017년 3월 16일

Deep Learning을 이용한 쿠즈시지 OCR

페이스북의 ‘월간 자연어 처리’ 페이지에서 흥미로운 이야기[1]를 들었다.

쿠즈시지(崩し字)라는 일본 전통 필기체가 있는 줄 처음 알았다. ㅎㅎㅎ 천년 이상 많은 고문서에서 쓰였던 모양인데, 현대 문자와 모양이 많이 달라서인지 훈련을 받지 않은 현대 일본인들도 읽을 수 없다고 한다. 일본 고전문학 전공하는 사람들이 많이 공부하는 듯. KuLA라고 하는 쿠즈시지 학습을 돕기위한 앱[2,3]도 있다. 오사카 대학에서 만든 앱이라고 한다.[4]

여하간 이 쿠즈시지 문자를 Deep Learning을 이용하여 OCR을 하려는 시도[5]가 있는 듯 하다. KuroNet이라고 이름도 붙인 듯. ㅎㅎ

검색해보니 이미 웹상에서 KuroNet을 시험해 볼 수 있는 서비스[6]가 있는 듯 하다. 회원등록을 해야 하는 듯. 근데 나에게 쿠즈시지 문서가 없어서 시험해 볼 수는 없구만-_- 그 밖에 detexify[7]처럼 필기인식을 지원하는 사이트[8]도 있는 듯 하다. 이 사이트[8]는 AI를 이용한다고는 돼 있는데, 어느 알고리즘을 쓰는지는 명확하게 밝히지 않고 있다. 개인이 운영한다고 한다.

여하간 KuroNet의 정확도가 꽤나 궁금해지는데, 논문[5]을 대충 봤다. F1 score라는 말이 나오던데, 이게 뭔 말인지 처음 알았다-_- 어느 블로그[9]에서 잘 설명해주고 있다. 여하간 높으면 좋은 것 같구만-_- 논문의 table 1에 책 별로 F1 score가 나와 있던데, 대부분의 책이 0.7~0.8 근처다. 애매하게 정확한데-_- 이걸 일일이 사람이 검토해야 된다면, 그냥 사람이 하는 것에 비해 효율이 좋지는 않을 듯 하다.

내 생각에는, 일전에 키보드 치는 소리로 패스워드를 빼내는 해킹을 하는 연구[10]가 생각나는데, 거기처럼 빈도분석법 + 철자교정 콤보를 쓰면 정확도가 꽤 많이 올라가지 않을까 싶기도 하다.

일반적인 상용 OCR 성능도 그럭저럭 쓸만하던데, 내 짐작으로는 그냥 텍스트만 있는 문서면 아마 F1 score가 0.7이상은 될 것 같다. 꼭 쿠즈시지가 아니더라도 상용 OCR과 딥 러닝을 활용한 OCR 사이의 성능격차가 유의미해야 이런 접근법이 의미가 있지 않을까 싶기도 하다.

.


[1] https://www.facebook.com/monthly.nlp/videos/474009179914214/
[2] くずし字学習支援アプリKuLA (app store)
[3] くずし字学習支援アプリKuLA (google playstore)
[4] Learning Japanese ancient characters with your smartphone (resou.osaka-u.ac.jp)
[5] “KuroNet: Pre-Modern Japanese Kuzushiji Character Recognition with Deep Learning”, Tarin Clanuwat, Alex Lamb, Asanobu Kitamoto, arXiv:1910.09433 [cs.CV]
[6] KuroNetくずし字認識サービス (mp.ex.nii.ac.jp)
[7] Detexify (detexify.kirelabs.org)
[8] AI 手書きくずし字検索 (ai-kuzushiji.net)
[9] 분류모델 (Classification)의 성능 평가 (bcho.tistory.com)
[10] “Keyboard acoustic emanations revisited.” L. Zhuang, F. Zhou, and J. D. Tygar. ACM Transactions on Information and Systems Security, 13:1, October 2009, pp 3:1-3:26 doi 10.1145/1609956.1609959

moebuntu : 우분투 모에 테마 배포판

우분투 기반에 모에 테마를 사용하는 배포판인 모에분투[1]라는 걸 봤다. 예상대로 역시나 일본인이 제작한 듯. ㅎㅎㅎ

대충보니, 아무래도 우분투와 기능적 차이가 있지는 않은 듯 하다.

나는 설치를 안 해봤지만, 유튜브에 구동영상이 몇 개 올라와 있어 참고할 수 있다.[2] 기회가 되면 깔아서 써 봐야지. 현재까지도 유지관리 되고 있는 듯 하다.

일전에 우분츄라는 우분투 만화[3]를 본 기억이 나는데, 다국어로 제작 가능하도록 만화를 오픈소스로 만든, 재미있는 시도를 하는 만화다. 근데 이건 더 이상 제작되지 않아 아쉽구만.

.


[1] moebuntu (moebuntu.web.fc2.com)
[2] “moebuntu” search result (youtube.com)
[3] AERIAL LINE (seotch.wordpress.com)

수학 증명 언어 Lean과 Buzzard 선생이 꿈꾸는 수학의 미래

해커뉴스[1]를 보니, 일전에 이야기한 수학 매거진 chalkdust[2]에 Kevin Buzzard 선생이 기고한 글[3]이 올라와 있던데, 아무래도 본인의 글이다보니 일전에 본 기사[4]보다는 좀 더 자세한 내용이 있다. waffiic님의 말 대로, 진짜 Buzzard 선생은 ZFC에서 와일즈-테일러 정리까지의 검증을 원하는 듯 하다. 참고로 Buzzard 선생이 블로그[5]를 하는 줄은 몰랐다. 팔로우해 볼만할 듯 하다. ㅎㅎㅎ

기고된 글[3]은 전반적으로 마이크로소프트에서 개발한 수학증명을 위한 프로그래밍 언어인 Lean을 소개하는 글 같은데, 재미있는 내용은 글의 뒷부분에 있다. 부르바키의 책 Topologie Générale의 챕터 2, 3을 게임으로 만들었다는 언급이 있던데, 이 게임 함 해볼 수 있나??? 싶어서 검색을 해봤는데, 공개되지는 않은 듯?? 상대성 이론 게임[6]보다 재미있으려나? ㅋㅋㅋ 나는 염불보다 잿밥에 더 관심이 있다. ㅋㅋ

(a+b)^3 = a^3 + 3a^2 b + 3ab^2 +b^3이 ring의 axiom으로부터 유도하는게 깜짝 놀랄정도로 어렵다고 나와 있던데, 과정은 귀찮아도 어렵지는 않을 듯 한데 좀 이상한 언급이다.

Peter Scholze 선생이 만들었다는 Perfectoid space 이야기는 처음 들었는데, 새로운 structure를 갖는 space인 줄은 알겠는데, 왜 중요한지는 잘 모르겠지만 뭔가 신박한 듯? -_-

유튜브에 Buzzard 선생의 강연[7]이 있던데, 길어서 다 보지는 않았지만 그가 상상하는 수학의 미래가 어떤 건지 대충 감이 온다. 진짜 옛날에 공상과학 영화에서 나오는 일상의 문제가 자동화 되어 해결되는 그런 머신스러운 세상의 수학버전인 듯 한 느낌이다. 어쩌면 삼위일체의 세상[8]이 올런지도 모르지. ㅎㅎ

.


2019.11.1
아참 그러고보니 lean을 이용하여 IMO 문제를 인공지능으로 해결해보려는 시도가 있는 듯.[9] 이거 이야기 하려는 거 깜빡했네. ㅋㅋ

.


[1] Can Computers Prove Theorems? (hacker news)
[2] 내 백과사전 수학적 호기심을 위한 매거진 : chalkdust 2017년 12월 16일
[3] chalkdust Can computers prove theorems? 23 October 2019
[4] 내 백과사전 참이라고 알려진 많은 수학 정리들이 거짓으로 판정될 가능성에 대해 2019년 9월 29일
[5] https://xenaproject.wordpress.com
[6] 내 백과사전 느린 광속 게임 A Slower Speed of Light 2012년 11월 8일
[7] The Future of Mathematics? (youtube 1시간 14분 47초)
[8] 내 백과사전 계산 삼위일체론 computational trinitarianism 2019년 6월 6일
[9] IMO Grand Challenge (imo-grand-challenge.github.io)

Seam Carving : 콘텐츠 기반 자동 이미지 크기 조정

해커뉴스에서 Seam carving이라는 알고리즘[1]이 화제[2]가 되고 있길래, 이게 뭔가 싶어서 검색을 좀 해 봤다. 헐 신박하구만. ㅋㅋㅋㅋ

이것을 잘 설명하는 블로그[3]나 영상[4,5]이 많다. 재생시간 4분 27초, 9분 9초

원래 신박한 아이디어는 콜롬버스 달걀 같은 거라서 처음 생각해 내기가 어렵지, 알고보면 쉬워 보인다-_-

알고리즘 구조상, 비슷한 픽셀이 이어지는 풍경화에는 잘 통하겠지만, 사진 전체에 걸쳐 변화무쌍한 구성인 상황에는 잘 안 통할 듯 하다. 그 경우는 영상[4]에서 사람을 지우는 예시 처럼 특정부위의 에너지 함수값을 수작업 세팅하는 과정이 필요한 것 같다.

이 기법을 데이터를 솎아내야 하는 다른 분야에 적용하면 안되나 싶은 생각이 좀 든다 ㅎㅎ

근데 논문[1] 중간에 커플을 솔로 사진으로 바꾸는 예시는 왠지 저자의 숨은 악의(?)가 있는 게 아닐까 하는 망상-_-이 든다 ㅋㅋㅋ

.


[1] “Seam Carving for Content-Aware Image Resizing”, Shai Avidan, Ariel Shamir, ACM Transactions on Graphics, Volume 26, Number 3, July 2007, Article No. 10 doi:10.1145/1276377.1276390
[2] Seam Carving (hacker news)
[3] Seam Carving – 내용을 인식하는 이미지 크기변환 방법 (infovis.tistory.com)
[4] Image Resizing by Seam Carving (youtube 4분 27초)
[5] 최소 손실 사진 크기 리사이즈 – Python (youtube 9분 9초)

이미지로 된 수식을 아래 한/글에 입력하는 스크립트

수학문제를 이미지 파일로 가지고 있을 때, 일전에 이야기한 mathpix[1]으로 수식을 LaTeX으로 변환할 수 있다. 이 변환결과를 입력하여 아래 한/글에서 수식으로 변환하여 입력하는 스크립트다.

동일한 스크립트를 여러 다른상황에서 쓸 수 있도록 만들었음. 본인은 한/글 2010을 쓰고 있어서, 다른 버전에서 작동을 보증하지 않음.

function OnScriptMacro_mathpix_ocr()
{
	var text = determine_eq_ob_nothing();

	if ( text == 'nothing')		//커서가 텍스트 입력상태일 때
	{
		text = GetTextFile("UNICODE","saveblock");

		if( text )		//텍스트가 블럭선택되어 있는 상태일 때
		{

			//이 부분에 넣고 싶은 기능을 넣으면 됨. ㅋㅋㅋㅋ

		}
		else		//텍스트 블럭이 아닌 상태 mathpix
		{
			var script = new ActiveXObject('MSScriptControl.ScriptControl');
			script.language = 'vbscript';
			var text = script.eval('inputbox("mathpix","입력")');

			//frac 처리
			var frac1 = text.search(/\\frac/), frac2, stop_inf=0;

			while( frac1> -1 && stop_inf < 40)
			{
				frac2 = braces_match(text, frac1);

				if(frac2 > -1)
				{
					text = text.substring(0,frac1) + text.substring(frac1,frac2).replace(/^\\frac/,'') + 'over' + text.substring(frac2);
					frac1 = text.search(/\\frac/)
				}
				stop_inf++;
			}

			//case 처리
			if ( text.search(/\\left\\\{\\begin\{array\}/) > -1)
			{
				text=text.replace(/\\left\\\{\\begin\{array\}\{[^\}]*\}/,'cases{');
				text=text.replace(/\\end\{array\}\\right\./,'}');
				text=text.replace(/\\\\/g,'#');
			}
			if( /\\left\\\{\\begin\{aligned\}/.test(text) )
			{
				text=text.replace(/\\left\\\{\\begin\{aligned\}/,'cases{');
				text=text.replace(/\\end\{aligned\}\\right\./,'}');
				text=text.replace(/\\\\/g,'#');
			}


			//align처리
			if( /^\\begin\{aligned\}/.test(text) )
			{
				text=text.replace(/^\\begin\{aligned\}/,'');
				text=text.replace(/\\end\{aligned\}$/,'');
				text=text.replace(/\\\\\s*([^&=\+\-\s])/g,', $1');		//식이 이어지고 있다고 추정되는 문자에는 콤마 뺌
				text=text.replace(/(\\\\)?\s*\&/g,'');
			}
			if ( text.search(/^\s*\\begin\{array\}/) > -1)		//식들의 병렬 배치
			{
				text=text.replace(/\\begin\{array\}\{[^\}]*\}\{/,'');
				text=text.replace(/\}\\end\{array\}/,'');

					//뒤에 quad를 기준으로 끊는 부분 있음		식이 끊어지는지 이어지는지 판정
				text=text.replace(/\}\s*\\\\\s*\{\s*\\quad\s*([\+=<>])/g,' $1');
				text=text.replace(/\}\s*\\\\\s*\{\s*\\quad\s*([^\+=<>\s])/g,' quad $1');		//마이너스는 식이 끊어지는 경우 있으므로 불포함

				text=text.replace(/\}\s*\\\\\s*\{\s*([\+=<>])/g,' $1');
				text=text.replace(/\}\s*\\\\\s*\{\s*([^\+=<>\s])/g,' quad $1');		//마이너스는 식이 끊어지는 경우 있으므로 불포함

			}

			//tex 기호를 한글 기호로 
			text=text.replace(/_\{([^\{\}]+)\}\s*\\mathbf\{C\}_\{([^\{\}]+)\}/g,'_{$1}{rm C}_{$2}');			//콤비네이션 처리
			text=text.replace(/\\boldsymbol\{([^\}]+)\}/g,'$1');		//볼드체 제거
			text=text.replace(/\\mathbf\{([^\}]+)\}/g,'$1');		//볼드체 제거
			text=text.replace(/\\geqq?/g,' ge ');
			text=text.replace(/\\leqq?/g,' le ');
			text=text.replace(/\\rightarrow/g,'->');
			text=text.replace(/\\pm/g,'+-');
			text=text.replace(/\\mp/g,'-+');
			text=text.replace(/\^\{((\\prime\s*)+)\}/g,'$1');
			text=text.replace(/\\overline/g,' bar');
			text=text.replace(/(left)?\\\{/g,' left{');
			text=text.replace(/(right)?\\\}/g,' right}');
			text=text.replace(/\^\{\s*\\circ\}/g,' DEG ');
			text=text.replace(/\\equiv/g,' == ');
			text=text.replace(/(^|\{)_/g,'$1`_');		//콤비네이션
			text=text.replace(/\\operatorname\{([^\}]+)\}/g,'$1');			//operatorname 제거
			text=text.replace(/<\-/g,'< -');							//좌측 화살표<-는 없다고 가정
			text=text.replace(/lim\s*_\s*\{([^\-]+)\->\s*([\+\-])([^\}]+)\}/g,'lim_{$1->$3$2}');		//lim_{x->+0}을 lim_{x->0+}로 변경
			text=text.replace(/\\,/g,'`');

			if( /\\overrightarrow/.test(text) )
			{
				text=text.replace(/\\overrightarrow/g,' vec');
				text=text.replace(/\\cdot/g,' bullet ');
			}


			//\Leftrightarrow 처리는 eqprint 함수 안에서


			//rm 처리
			text=text.replace(/\\mathrm\{/g,'{rm ');
			text=text.replace(/(vec|bar)\s*\{\s*([A-Z])\s*(_\{[0-9]\})?\s*([A-Z])\s*(_\{[0-9]\})?/g,'$1{rm $2$3$4$5');

			text=text.replace(/\{\{([^\{\}]*)\}\}/g,'{$1}');		//괄호 중복 제거
			text=text.replace('','');			//전각 공백 제거
			text=text.replace(/\\/g,' ');


			var seperated_equation = text.split('quad'), i, pos, stop_inf = 0;

			for(i = 0; i< seperated_equation.length-1 && stop_inf < 20; i++)		//만약 quad로 나누어 진다고 하더라도 중괄호가 안 맞으면 결합함
			{
				pos = braces_match(seperated_equation[i]);
				if ( /[\}\{]/.test(seperated_equation[i]) && pos == -1)
				{
					seperated_equation[i] = seperated_equation[i] + seperated_equation[i+1];
					seperated_equation.splice(i+1, 1);
				}

				stop_inf++;
			}

			for(i = 0; i< seperated_equation.length; i++)
			{
				if( /\s*,\s*`\s*$/.test(seperated_equation[i]) )
				{
					eqprint( seperated_equation[i].replace(/\s*,\s*`\s*$/, '') );
					print(', ');
				}
				else if( /^\s*\(.*\)\s{0,}$/.test(seperated_equation[i]) )
				{
					print(' (');
					eqprint( seperated_equation[i].replace(/^\s*\((.*)\)\s{0,}$/, '$1') );
					print(')');
				}
				else
				{
					eqprint( seperated_equation[i] );
				}
			}
		}
	}
	else if ( text )		//수식 개체가 선택된 경우
	{

			//이 부분에 넣고 싶은 기능을 넣으면 됨. 수식 내용을 변경한다든지 등등등.

	}
	else		//그리기 개체가 선택된 경우
	{

			//이 부분에 넣고 싶은 기능을 넣으면 됨. 그리기 개체 사이즈를 통일한다든지 등등등.
/*
		HAction.GetDefault("ShapeObjDialog", HParameterSet.HShapeObject.HSet)
		with (HParameterSet.HShapeObject)
		{


		}
		HAction.Execute("ShapeObjDialog", HParameterSet.HShapeObject.HSet);
*/
	}

}

/////////////
////서브루틴
//현재 커서에 수식이 선택되면 수식 텍스트 반환, 그리기 개체가 선택되면 ''반환, 아무것도 아니면 'nothing'반환
function determine_eq_ob_nothing()
{
	if (HAction.GetDefault("EquationPropertyDialog", HParameterSet.HShapeObject.HSet) )	//수식, 개체는 true
	{
		with (HParameterSet.HShapeObject)
		{
			text = String;
		}
		HAction.Execute("EquationPropertyDialog", HParameterSet.HShapeObject.HSet);

		//그리기 개체일 때, 이 부분에 print 함수 쓰면 한/글 비정상 종료됨

		return text;
	}
	return 'nothing';		//현재 커서에 오브젝트가 선택되지 않은 경우
}


//출력 함수
function print(text)
{
	HAction.GetDefault("InsertText", HParameterSet.HInsertText.HSet);
	HParameterSet.HInsertText.Text = text;
	HAction.Execute("InsertText", HParameterSet.HInsertText.HSet);
}


//수식 출력함수
function eqprint(text)
{
	var arr = text.split(/Leftrightarrow/), i;

	for(i=0; i<arr.length; i++)
	{
		if( i !=0 )
		{
			print( ' ⇔ ');
		}

		HAction.GetDefault("EquationCreate", HParameterSet.HEqEdit.HSet);
		with (HParameterSet.HEqEdit)
		{
			BaseUnit = PointToHwpUnit(10.0);
			String = arr[i];
		}
		HAction.Execute("EquationCreate", HParameterSet.HEqEdit.HSet);
	}
}


//문자열(text)과 시작위치(frac1)을 입력하면 이후 중괄호 짝이 맞는 최대 문자열 위치를 반환한다.	괄호가 없거나 안 맞으면 -1 반환
function braces_match(text, frac1)
{
	var counter = 1;

	var frac2 = text.indexOf('{', frac1);		//최초 스타트 상태에서 괄호의 개수가 영개이므로 처음 괄호가 시작되는 위치를 찾는다.
	if (frac2 == -1)		//에러 처리
	{
		return -1;
	}

	for(i=frac2+1; i<text.length && counter > 0; i++)
	{
		if( text.charAt(i) == '{' )
		{
			counter++;
		}
		if( text.charAt(i) == '}' )
		{
			counter--;
		}
	}

	if ( counter<0 || (i == text.length && counter != 0) )			//에러 처리
	{
		return -1;
	}
	return i;
}

inputbox 불러내서 입력하는 부분에서, 입력 글자제한이 있어서, 너무 긴 수식은 입력 안 되는 수가 있음.

.


[1] 내 백과사전 Mathpix : 수식 이미지를 LaTeX으로 자동변환하는 프로그램 2019년 4월 11일

검색엔진의 새로운 시대적 전환 : 제로 클릭 검색

해커 뉴스[1]를 보니 구글 검색에서 제로 클릭 검색이 전체 검색 트래픽의 절반을 초과했다는 소식이 있었다.

나는 ‘Zero Click Searches’라는 용어를 처음 들었는데, 이게 뭔 뜻인가 싶어 검색해보니, 용어만 몰랐지 사실 나도 예전부터 엄청 쓰고 있었던 기능이었다. 검색엔진에 검색한 이후에 원하는 정보를 얻었기 때문에, 굳이 검색 결과를 클릭하지 않고 넘어가는 상황을 의미한다고 한다. 예를 들어, 개인적으로 엑셀을 사용해야 하는 상황이 종종 생기는데, 귀차니즘 때문에-_- 무조건 함수와 스크립트로 자동화를 하는 경향이 있다. 근데 엑셀의 모든 스크립트와 함수를 알지 못하므로 검색에 의존하는데, 이 경우 예를 들어, ‘how to sum excel’ 따위를 구글링하면, 굳이 웹페이지를 클릭하여 보지 않고도 구글이 알아서 방법을 설명해 주기 때문에 검색 결과를 클릭할 필요가 없다.

이 제로 클릭 검색이 전체 검색의 절반을 돌파했다는 소식을 봤는데, 이제 슬슬 검색 엔진의 큰 패러다임이 바뀌는 게 아닌가 싶은 생각이 든다. 과거 page rank 알고리즘이라는 혁신적인 방법을 통하여 구글이 검색엔진의 성배를 석권한 이래로 검색엔진계를 재패해 왔는데, 이제 그런 시대는 가고 구글도 검색엔진으로서 정체성을 새롭게 확립해야 하는 게 아닌가 싶기도 하다.

생각해보면 그런 의미에서 이러한 스니펫을 일찌감치 도입한 네이버나 다음이 실질적으로는 시대를 앞서간(?) 검색엔진이 아니었나 싶은 생각도 들기는 한다. ㅎㅎㅎ 물론 인력에 의한 스니펫이 아니라 인공지능에 의한 스니펫이 앞으로는 검색엔진의 대세가 될 듯 하다. 우리 후대에서는 좀 더 자연어로서의 질문에 반응하고, 더 자연스러운 대답을 추구하는 검색엔진이 될 것이라는 점에서 구글신[2]에 더 근접하지 않을까 하는 망상이 든다. ㅋㅋㅋ 뭐, 술먹고 쓰는 글이라 개소리라 생각해주시길 바란다. ㅎ 여하간 내가 노인이 되었을 때의 검색엔진의 모습은 지금과는 또 사뭇 다를 듯 하다.

.


2019.10.21
위 글과 무관한 내용이긴한데, 딱히 포스팅 하나로 쓰기에는 영양가가 없어서 첨가함. ㅋ

구글 검색엔진에서 ‘recursion’을 검색하면 정정 제안으로 다시 ‘recursion’을 제시한다는 이야기를 듣고 검색해봤다.


진짜네-_- 이거 의도한건지 궁금해진다. ㅎㅎㅎ

.


[1] Fewer Than Half of Google Searches Now Result in a Click (hacker news)
[2] 내 백과사전 구글교 Church of Google 2010년 11월 19일

음악 추천 알고리즘 : Hierarchical Poincaré Embeddings

해커 뉴스[1]에서 Hierarchical Poincaré Embeddings를 이용하여 음악 추천을 하는 이야기를 봤는데, 뭔 소리인지는 잘 모르겠지만-_- 기록차 남겨둠. 나는 이렇게 이해했는데, 아무래도 이 포스트는 오류를 포함하고 있을 가능성이 매우 높다. ㅋㅋ

일전에 Matrix factorization 이야기[2]를 했는데, 뭐 잘 모르지만 꽤나 광범위하게 쓰이는 추천 알고리즘 같다.

기계 학습을 시킬 때 학습대상의 특징을 뽑아내서 분류하고 학습하는 방식을 많이 쓰는 모양인데, 이 때 neighbor를 판정하는 방법으로 그냥 Euclidean space를 많이 쓰는 듯 하다. Matrix factorization도 오류 판정을 root mean squared를 일반적으로 쓰는 듯 한데, 이것도 일종의 Euclidean이라 봐야 할 듯 하다.

그런데 페이스북 소속 연구원 2명이 계층적 구조를 가진 데이터를 학습할 때 Euclidean 대신 Poincaré ball을 쓰면 더 나은 결과가 될 듯 하다는 주장을 하는 듯한-_- 글[3]을 대충 봤다. 아무래도 실제 회사들의 데이터들에서는 Zipf’s law처럼 값들이 한쪽에 쏠려있는 경우도 많으니까 그런 듯?

iHeartRadio라는 인터넷 라디오 방송 플랫폼이 있다고 한다. 나는 처음 들었는데 나름 꽤 큰 회사인 듯? 이 iHeartRadio 소속 4명의 연구원들이 Poincaré ball이 낫다는 주장[3]을 보고, 음악 추천에 시험해 본 듯 하다.[4] 음악의 메타데이터가 ‘장르-아티스트-곡명’ 과 같은 계층적 데이터라서 적용가능한 듯?

예를 들어 Matrix factorization으로 추천 목록을 만들면

The Shins – September
Lilly Hiatt – Jesus Would’ve Let Me Pick
METRIK – We Got It
Matrix & Futurebound – Magnetic Eyes
Dads – Dads (feat. Berried Alive & Lucas Mann)
Ugly Casanova – Spilled Milk Factory
Cursive – Ouroboros
Reggie and the Full Effect – Your Girlfriends
Hey Mercedes – What You’re Up Against
The Blood Brothers – Laser Life

와 같은 곡들이 추천되고, Poincaré 모델로 추천하면

The Strokes – Taken for a Fool
Arctic Monkeys – Brianstorm
The Strokes – 12:51
The Fratellis – Tell Me A Lie
Kings of Leon – Crawl
The Strokes – Is This It
Franz Ferdinand – Ulysses
Cage the Elephant – Shake Me Down (Unpeeled)
Death Cab for Cutie – Stay Young, Go Dancing
Kings of Leon – Notion

와 같은 곡들이 추천된다고 한다. 딱 봐도 완전 판이한 결과가 나온다. 근데 아는 아티스트가 하나도 없네-_- 요새는 맨날 아이돌 마스터 음악만 들어서…-_-

여하간 iHeartRadio에서는 사용자를 두 그룹으로 나누어 한 쪽은 푸앵카레 모델에 기반한 음악을 추천하고(treatment 그룹), 다른 한 쪽은 기존의 행렬 인수분해 기법에 기반한 음악을 추천하였더니(control 그룹), 평균 음악을 듣는 시간이 다음과 같게 나왔다고 한다.[4;p5]

헐.. 여러모로 월등히 우월하네. 현재 iHeartRadio에서는 푸앵카레 모델에 기반하여 디폴트 플레이리스트 생성기가 작동된다고 한다.

Matrix factorization을 이용한 학습 추천 시스템이 나름 꽤 널리 쓰인다고 들었는데, 산업의 종류에 따라서는 Poincaré model로 학습해서 추천하는 걸로 빠르게 바뀌지 않을까 싶은 생각도 든다. ㅎㅎ

.


[1] Music Recommendations in Hyperbolic Space (hacker news)
[2] 내 백과사전 영화 추천 알고리즘 : Matrix factorization 2019년 8월 4일
[3] “Poincaré Embeddings for Learning Hierarchical Representations”, Maximilian Nickel, Douwe Kiela arXiv:1705.08039 [cs.AI]
[4] “Music Recommendations in Hyperbolic Space: An Application of Empirical Bayes and Hierarchical Poincaré Embeddings”, Tim Schmeier, Sam Garrett, Joseph Chisari, Brett Vintch arXiv:1907.12378 [cs.IR]