GIZA++ on Mac OS X

Now I can install GIZA++ on Mac OS X by just typing “sudo port install giza-pp”. It is very convenient. However the file system on the machine I’m using isn’t case sensitive. So *.a3.final and *.A3.final are the same file.  

In order to solve this, I decided to install giza-pp 1.0.7 from source code instead and modify model3.cc like I did in 2006 on my iBook G4 and it still works ^^.

I used to use UFS instead of HFS+. And I also found in Wikipedia that HFS+ can be case sensitive too. But I think it is not good idea to format or modify too much the machine that is not mine :-P.

GIZA++ 1.0.3 บน Ubuntu 9.10

ผมพยายามจะลง Giza++ 1.0.3 บน Ubuntu 9.10 จริงๆ มี .deb ให้ใช้แต่ว่า ผมไม่ได้ใช้เพราะอยากจะ code ของ Giza++ ด้วย

แต่ว่าก็มีปัญหานิดหน่อย เพราะว่า string ที่จองมาเก็บปี จองมาน้อยไปหน่อย คล้ายๆ ปัญหา y2k ก็เลย patch ไปแบบนี้

diff -Nur giza-pp/GIZA++-v2/file_spec.h giza-pp.orig/GIZA++-v2/file_spec.h
--- giza-pp/GIZA++-v2/file_spec.h	2010-02-06 19:05:55.000000000 +0000
+++ giza-pp.orig/GIZA++-v2/file_spec.h	2009-03-20 11:41:12.000000000 +0000
@@ -37,13 +37,13 @@
   struct tm *local;
   time_t t;
   char *user;
-  char time_stmp[18];
+  char time_stmp[17];
   char *file_spec = 0;
   
   t = time(NULL);
   local = localtime(&t);
   
-  sprintf(time_stmp, "%03d-%02d-%02d.%02d%02d%02d.", local->tm_year, 
+  sprintf(time_stmp, "%02d-%02d-%02d.%02d%02d%02d.", local->tm_year, 
 	  (local->tm_mon + 1), local->tm_mday, local->tm_hour, 
 	  local->tm_min, local->tm_sec);
   user = getenv("USER");

รายงานบักไปแล้วเหมือนจะซ้ำด้วย http://code.google.com/p/giza-pp/issues/detail?id=20 บรรยากาศก็เงียบๆ

เตรียม parallel corpus สำหรับ word alignment จาก .po

วิธีที่ก็ใช้ polib อ่าน .po ของ GNOME มาแล้วก็พยายาม ตัดเครื่องหมายทิ้ง. แม้แต่ string ที่มีหลายบรรทัดก็ตัดทิ้ง.

ขั้นแรกเลยคือเข้าไปที่ web L10N ของ GNOME http://l10n.gnome.org/languages/th/gnome-2-22 หลังจาก copy & paste และแก้ด้วยความถึกนิดหน่อย ผมก็ได้รายการของ .po มา.

Download po file pessulus       100% (30/0/0)
Download po file Sabayon        100% (230/0/0)
GNOME developer platform (66% translated)
Download po file atk    95% (117/0/6)
Download po file gail   100% (103/0/0)

พอได้แบบนี้มาแล้วก็เขียนโปรแกรมมาตัดๆ หน่อย

get_pkg.rb

while gets
    if $_ =~ /Download po file ([^s]+)/
        print "wget  http://l10n.gnome.org/POT/#{$1}.HEAD/#{$1}.HEAD.th.pon"
    end
end

ก็ได้ script ที่ load .po ออกมา

$ ruby get_pkg.rb > download.sh && sh download.sh

พอได้แบบนี้มาแล้วผมก็เขียนโปรแกรมมา extract ของใน .po มาชื่อ ext_po.py


#-*- coding: UTF-8 -*-
import sys
import polib
import getopt
import re

class Params:
    def __init__(self, o_eng_path, o_tha_path, i_po_paths):
        self.o_eng_path = o_eng_path
        self.o_tha_path = o_tha_path
        self.i_po_paths = i_po_paths

    def __str__(self):
        return str(self.__dict__)

def usage():
    print "Usage: " + sys.argv[0] + " -e  -t   ..."

def usage_and_exit():
    usage()
    sys.exit(2)

def get_params():
    try:
        opts, args = getopt.getopt(sys.argv[1:], "e:t:")
        keys = [o[0] for o in opts]
        if not "-e" in keys:
            print "Require English output filename"
            usage_and_exit()
        if not "-e" in keys:
            print "Require Thai output filename"
            usage_and_exit()
        if len(args) < 1:
            print "Require po filename"
            usage_and_exit()
        return Params(o_eng_path = filter(lambda o: o[0] == "-e", opts)[0][1],
                      o_tha_path = filter(lambda o: o[0] == "-t", opts)[0][1],
                      i_po_paths = args)
    except getopt.GetoptError, err:
        print str(err)
        usage_and_exit()

def entry_constraints(entry):
    return not entry.translated() and entry.msgstr != ""
        and entry.msgid != ""

def remove(txt, sym):
    return re.sub(sym, " ", txt)

def convert_text(txt):
    syms = ["_", "...", ":", "|", "-+", "/+", "%w", "",
            """, "©", "~", "(", ")"]
    return reduce(remove, syms, txt)

def split_line(txt):
    return filter(lambda tok: not re.match("^ *$", tok),
                  re.split("n", txt))

def split_line_in_entry(ans, entry):
    e_lines = split_line(entry[0])
    t_lines = split_line(entry[1])
    if len(e_lines) == 1 and len(t_lines) == 1:
        return ans + [(e_lines[i], t_lines[i]) for i in range(len(e_lines))]
    else:
        return ans

def ext_po_file(o_eng_file, o_tha_file, po):
    entries = filter(entry_constraints, list(po))
    entries = [(entry.msgid, entry.msgstr) for entry in entries]
    entries = [(convert_text(e[0]), convert_text(e[1])) for e in entries]
    entries = reduce(split_line_in_entry, entries, [])
    for entry in entries:
        o_eng_file.write(entry[0] + "n")
        o_tha_file.write(entry[1] + "n")

def ext_with_ofiles(o_eng_file, o_tha_file, i_po_paths):
    for i_po_path in i_po_paths:
        po = polib.pofile(i_po_path)
        ext_po_file(o_eng_file, o_tha_file, po)

def ext(o_eng_path, o_tha_path, i_po_paths):
    o_tha_file = open(o_tha_path, 'w')
    o_eng_file = open(o_eng_path, 'w')
    ext_with_ofiles(o_eng_file, o_tha_file, i_po_paths)
    o_eng_file.close()
    o_tha_file.close()

def init_charset():
    reload(sys)
    sys.setdefaultencoding('utf-8')

def main():
    init_charset()
    params = get_params()
    ext(params.o_eng_path, params.o_tha_path, params.i_po_paths)

if __name__ == '__main__':
	main()

จากนั้นก็สั่ง (โดยสมมุติว่า .po ทั้งหมดอยู่ใน folder เดียวกัน)

$ python ext_po.py -e e -t t  *.po

cut.sh เอาไว้ตัดคำโดยใช้  kucut อีกที

#!/bin/sh
iconv -f UTF-8 -t TIS-620 < $1 > $1.tis
kucut –line=” ” $1.tis
iconv -f TIS-620 -t UTF-8 < $1.tis.cut > $1.cut

แล้วก็ใช้ cut.sh โดยเรียก

$ ./cut.sh t && mv t.cut t

จากนั้นก็ไปเรียก GIZA++ แบบที่แก้ๆไปแล้ว ได้เลย

$ plain2snt e t

$ train-giza++ e.vcb t.vcb e_t.snt

เป็นอันเรียบร้อย

ได้ผลออกมาแบบนี้

# Sentence pair (1) source length 6 target length 6 alignment score : 0.000163118
รายการ เมนู ใหม่ ต้อง มี ชื่อ
NULL ({ }) New ({ 3 }) menu ({ 2 }) items ({ 1 }) need ({ 4 5 }) a ({ }) name ({ 6 })
# Sentence pair (2) source length 5 target length 5 alignment score : 0.00022357
เมนู ใหม่ ต้อง มี ชื่อ 

ผลอ่ายากนิดนึงแบบ New ({3}) หมายถึงว่า new ตรงกับคำภาษาไทยตัวที่ 3 ใน “รายการ เมนู ใหม่ ต้อง มี ชื่อ” ก็คือใหม่นั่นเอง.

Debian/Ubuntu package: GIZA++ 1.0.1

There is also Ubuntu packages of mkcls and GIZA++ at http://cl.aist-nara.ac.jp/~eric-n/ubuntu-nlp/dists/dapper/nlp/. However I want to use giza-pp (including, mkcls) 1.0.1 from http://code.google.com/p/giza-pp/. Thus I pack it. The results are as follow:

gizapp_1.0.1-1ubuntu5.diff.gz
gizapp_1.0.1-1ubuntu5.dsc
gizapp_1.0.1-1ubuntu5_i386.build
gizapp_1.0.1-1ubuntu5_i386.changes
gizapp_1.0.1.orig.tar.gz
giza++-static_1.0.1-1ubuntu5_i386.deb
mkcls_1.0.1-1ubuntu5_i386.deb

Changes note: There are some changes in command line interface as follow:

GIZA++ changed to giza++.
snt2plain.out changed to snt2plain.
plain2snt.out changed to plain2snt.
snt2cooc.out changed to snt2cooc.
trainGIZA++ changed to train-giza++.

Lintian reported many warnings but I still don’t know how to fix them :-P.

Update: To pass Lintian tests, man pages are needed.

Usage example

Given there 2 parallel plain  text files in English and Thai.

eng.txt:

a dog eat a chicken
a chichken eat a fish

tha.txt:

หมา กิน ไก่
ไก่ กิน ปลา

In order to align these text, we use this script as follow:

$ plain2snt eng.txt tha.txt
w1:eng w2:tha
eng -> eng
tha -> tha

$ train-giza++ eng.vcb tha.vcb eng_tha.snt
END.

Then the result, will be in GIZA++.A3.final :

$ cat GIZA++.A3.final
# Sentence pair (1) source length 5 target length 3 alignment score : 0.0373314
หมา กิน ไก่
NULL ({ }) a ({ }) dog ({ }) eat ({ }) a ({ 2 }) chicken ({ 1 3 })
# Sentence pair (2) source length 5 target length 3 alignment score : 0.0373315
ไก่ กิน ปลา
NULL ({ }) a ({ }) chichken ({ }) eat ({ }) a ({ 2 }) fish ({ 1 3 })

P.S. I built these packages on Ubuntu 7.10

การแปลภาษาไทย – อังกฤษโดยใช้สถิติ

การแปลภาษาโดยใช้สถิติ เดี๋ยวนี้มีโปรแกรม open source ให้ download กันมาใช้แล้ว. เช่น GIZA++ ที่เอาไว้เตรียม model และ Moses ที่เอาไว้แปล (decode). โดยใช้งานตาม diagram ข้างล่าง.

smt_flow.png

สิ่งที่เหมือนขาดไปคือ “โปรแกรมตัดประโยคภาษาไทย”. แต่ถ้าจะแปลอังกฤษเป็นไทยก็มีนะ. และคลังข้อความขนานที่มันใหญ่พอ.