veer66

veer66

โพสต์นี้คนอ่านที่เป็นเป้าหมายหลักก็คือผมเอง

อย่างแรกเลยสมมุติว่า ElasticSearch ที่รันอยู่แล้ว

สร้าง dir ที่จะเก็บ snapshot และ chown ให้เป็นของ user เดียวกับที่ daemon ของ elasicsearch รันอยู่

แก้ elasticsearch.yml ที่ path.repo ให้ชี้ไปที่ dir ที่สร้างไว้ในข้อ 1 เช่น [“/bak/b1”] ไรงี้

backup/snapshot

สร้าง repo

curl -X PUT "localhost:9200/_snapshot/my_backup?pretty" -H 'Content-Type: application/json' -d'
{
  "type": "fs",
  "settings": {
    "location": "/bak/b1"
  }
}
'

restore

สร้าง repo

curl -X PUT "localhost:9200/_snapshot/my_backup/snapshot_1?wait_for_completion=true&pretty"

ตอนนี้ก็คือจะเสร็จแล้ว สิ่งที่ผมทำคือ rsync /bak/b1 ไปเครื่องอื่นแล้วลอง restore เพื่อไม่ให้น่าเบื่อเครื่องอื่นที่ว่าก็จะใช้ docker ด้วย

พอ rsync มาแล้ว ก็ chown -R 1000:1000 $(pwd)/bak/b1

สร้าง elasticsearch.yml

cluster.name: "docker-cluster"
network.host: 0.0.0.0
path.repo: ["/usr/share/elasticsearch/bak"]

รัน ElasticSearch ผ่าน Docker

docker run \
       --name=es6 \
       --net=host \
       -e "discovery.type=single-node" \
       -v $(pwd)/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
       -v $(pwd)/bak/b1:/usr/share/elasticsearch/bak \
       docker.elastic.co/elasticsearch/elasticsearch:6.8.4

สร้าง repo

curl -X PUT "localhost:9200/_snapshot/my_backup?pretty" -H 'Content-Type: application/json' -d'
{
  "type": "fs",
  "settings": {
    "location": "/usr/share/elasticsearch/bak"
  }
}
'

สั่ง restore

curl -X PUT "localhost:9200/_snapshot/my_backup?pretty" -H 'Content-Type: application/json' -d'
{
  "type": "fs",
  "settings": {
    "location": "/usr/share/elasticsearch/bak"
  }
}
'

เสร็จแล้ว

คำสั่งต่าง ๆ ก็มาจาก guide ครับ

ผมทำตามตัวอย่างที่ https://tantivy-search.github.io/examples/basic_search.html แต่โมพวก field แล้วก็แก้ให้เป็นสองไฟล์แบบที่อยากใช้งาน

Cargo.toml

[package]
name = "v_search"
version = "0.1.0"
authors = ["Vee Satayamas <vee.sa@protonmail.com>"]
edition = "2018"

[dependencies]
tantivy = "0.10.2"

[[bin]]
name = "make_index"
path = "src/make_index.rs"

[[bin]]
name = "search"
path = "src/search.rs"

make_index.rs

ส่วนที่ผมงงแรก ๆ เลยคือ STORED อ่านมาบอกว่าสามารถอ่านข้อมูลคืนมาด้วย id เข้าใจว่าถ้า field ไหนไม่ STORED คือทำ index อย่างเดียวจริง ๆ แต่ข้อมูลต้นฉบับหาย แต่ผมก็ยังไม่ลองว่าถ้าไม่ใส่มันจะไม่ได้เชียวหรือ ?

use tantivy::doc;
use tantivy::schema::*;
use tantivy::Index;

fn main() -> tantivy::Result<()> {
    let index_path = "idx"; 
    let mut schema_builder = Schema::builder();
    schema_builder.add_text_field("textunit", TEXT | STORED);
    let schema = schema_builder.build();
    let textunit = schema.get_field("textunit").unwrap();
    let index = Index::create_in_dir(&index_path, schema.clone())?;
    let mut index_writer = index.writer(50_000_000)?;

    index_writer.add_document(doc!(
        textunit => "A",
    ));

    index_writer.commit()?;
    Ok(())
}

search.rs

use tantivy::collector::TopDocs;
use tantivy::query::QueryParser;
use tantivy::schema::*;
use tantivy::Index;
use tantivy::ReloadPolicy;

fn main() -> tantivy::Result<()> {
    let index_path = "idx"; 
    let mut schema_builder = Schema::builder();
    schema_builder.add_text_field("textunit", TEXT | STORED);
    let schema = schema_builder.build();
    let textunit = schema.get_field("textunit").unwrap();
    let index = Index::open_in_dir(index_path)?;
    let reader = index
        .reader_builder()
        .reload_policy(ReloadPolicy::OnCommit)
        .try_into()?;
    let searcher = reader.searcher();

    let query_parser = QueryParser::for_index(&index, vec![textunit]);
    let query = query_parser.parse_query("A")?;
    let top_docs = searcher.search(&query, &TopDocs::with_limit(10))?;
    for (_score, doc_address) in top_docs {
        let retrieved_doc = searcher.doc(doc_address)?;
        println!("{}", schema.to_json(&retrieved_doc));
    }
    Ok(())
}

อันนี้ผมก็คือทำ index ไว้ใน directory ชื่อ idx แล้ว แล้วอีกไฟล์ก็มา search แค่นี้เลย

code แค่นี้เลย

fn cnt_links(s: usize, e: usize, tu: &Textunit, en_sent: &RTok) -> usize {
    tu.links.iter()
        .filter(|link| link.target > s && link.target < e)
        .filter(|link| link.source >= en_sent.s && link.source < en_sent.e)
        .count()
}

พอเขียน test แบบยาว ก็เจอจุดพัง

#[test]                                                                                                                                                
    fn cnt_links_simple() {
        let tu = Textunit {
            bi_text: BiText { source: String::from(""),
                              target: String::from(""),
            },
            bi_rtoks: BiRToks {
                source: vec![RTok {s:100, e:101, text: String::from(" ")},
                             RTok {s:102, e:103, text: String::from(" ")},],
                target: vec![RTok {s:300, e:301, text: String::from(" ")},
                             RTok {s:302, e:303, text: String::from(" ")},],
            },
            links: vec![Link {source: 0, target: 1},
                        Link {source: 1, target: 0}],

        };
        let en_sent = RTok {s: 302, e:303, text: String::from(" ")};
        assert_eq!(cnt_links(0, 1, &tu, &en_sent), 1);
    }

ระบบ type ช่วยอะไรไม่ค่อยได้เพราะมันเป็น integer หมด ยกเว้นจะใช้ type แบบที่มีหน่วยได้ แต่แบบนั้นก็ไม่รู้ว่าโปรแกรมจะยาวกว่านี้อีกเปล่า

รู้สึกว่าปัญหาของโปรแกรมนี้หลัก ๆ เลยคือ test มันยาวเกิน

มันเป็น API อยู่ที่ https://cloud.google.com/speech-to-text/ มีวิธีใช้พร้อม ผมเปลี่ยน config นิดหน่อย ตามนี้ครับ

เขามีไฟล์ PDFให้อ่านด้วยนะครับ

แต่แค่อยากลองว่า Google มันจะฟังออกแค่ไหน

config = { encoding:          :FLAC,
           sample_rate_hertz: 16_000,
           language_code:     "th-TH" }

ไฟล์เสียง

เพื่อใช้ในการวิจัยและไม่ได้มีการหาผลประโยชน์อะไรเลยนะครับ ตามนี้ https://file.veer66.rocks/nlp/sample1min7.flac

วิธีเตรียมไฟล์เสียงเพื่องานวิจัยและไม่ได้มีการหาผลประโยชน์

อันนี้เอาไฟล์เตรียมไฟล์ video ก่อนเพื่องานวิจัยและไม่ได้มีการหาผลประโยชน์

ffmpeg -i http://live.parliament.go.th/livestream/encoder3/playlist.m3u8 -c copy -bsf:a aac_adtstoasc output.mp4

แล้วก็แปลงเป็นเสียงเพื่องานวิจัยและไม่ได้มีการหาผลประโยชน์

ffmpeg -i output.mp4 sample.mp3

หลังจากนั้นผมใช้ Audacity แก้เสียง sample.mp3 ให้เป็น mono 16-bit แล้ว save เป็น .flac ครับ เพื่องานวิจัยและไม่ได้มีการหาผลประโยชน์อีกเช่นเคย

ผลออกมาได้งี้

ชาติแห่งชาติขุนกลางยึดมั่นในหลักจริยธรรมและธรรมาภิบาลมีสมรรถนะและความรู้ความสามารถพร้อมต่อการเปิดงานดำเนินการปรับปรุงสวัสดิการสวัสดิภาพชีวิตขวัญกำลังใจความเป็นอยู่ให้เกิดความปลอดภัยในการทำงาน

I guess many people wrote this kind of scripts already. But sometimes searching is more complicated than just writing a new one. 😅 If you know a keyword, please tell me.

This script should work with other commands. I use target/release/main because I want to test my program built from Rust's Cargo. I suppose other commands can replace target/release/main.

#!/bin/sh                                                                                                                                                                   
                                                                                                                                                                            
CMD=target/release/main                                                                                                                                                     
                                                                                                                                                                            
$CMD > log 2>&1 &                                                                                                                                                           
export PID=$!                                                                                                                                                               
echo "Process $PID is running ..."                                                                                                                                          
                                                                                                                                                                            
trap "echo kill $PID; kill $PID; exit" INT QUIT                                                                                                                             
                                                                                                                                                                            
rm -f mem.tsv                                                                                                                                                               
while true                                                                                                                                                                  
do                                                                                                                                                                          
    tm=`date '+%Y-%m-%dT%H:%M:%S'`                                                                                                                                          
    rss=`ps -u | awk -v PID=$PID '{ if ($2 == PID) { print $6; } }'`                                                                                                        
    echo "$tm\t$rss" >> mem.tsv                                                                                                                                             
    sleep 10                                                                                                                                                                
done                                                                                                                                                                        

It logs memory usage in mem.tsv, so I should be able to plot a graph by LibreOffice Calc easily.

When the producer sends messages too frequently, and the producer takes too much time to process each message. I wrote a code for

I want to discard aging messages and process only the latest one. So I wrote a code for repeating receive messages until the channel is empty for a short duration. However, I found my code was too complicated. So I use shared memory with mutex-lock for inter-thread communication instead of a channel.

use std::sync::{Mutex, Arc};
use std::{thread, time};
use std::collections::HashMap;

fn main() {
    let r_shared_data = Arc::new(Mutex::new(None));
    let w_shared_data = r_shared_data.clone();
    
    thread::spawn(move || {
        let tx_delay_time = time::Duration::from_millis(100);
        loop {
            for i in 1..1000 {
                let mut h = HashMap::new();
                h.insert("X", i);
                let mut w = w_shared_data.lock().unwrap();
                *w = Some(h);
                if i % 10 == 0 {
                   thread::sleep(tx_delay_time);
                }                
            }
        }
    });

    let task_proc_time = time::Duration::from_millis(500);
    loop {
         let r = r_shared_data.lock().unwrap();
        dbg!(r);
        // long task
        thread::sleep(task_proc_time);
    }
}

เดิมโพสต์เมื่อ 2015-06-25

ภาษาใหม่ ๆ เดี๋ยวนี้ ทั้ง Rust, Swift, F#, Scala หรือภาษากลางเก่ากลางใหม่แบบ C#, Java 8, Python, Ruby, JavaScript มักจะมีกลิ่นอายของ Lisp/Scheme ติดมาด้วยเสมอเช่นการ ใช้ closure กับ map, foldl, foldr, filter เป็นต้น

เนื่องจากว่าเขียนได้ทั้งแนว Fortran และแนว Lisp เวลาคนเขียนภาษาใหม่ ๆ เช่น Python บางทีก็จะเถียงกัน (ผมเห็นจากใน blog ของคนอื่น) ว่าจะใช้ map filter reduce ทำไม อ่านยากจะแย่ จะเขียนสั้น ๆ ทำไม ต้องใช้ for-loop แทนสิ บางคนก็บอกว่า map filter reduce มันก็ดีอยู่แล้ว เขียน for-loop มันอ่านง่ายตรงไหน

ส่วนภาษา Go เป็นภาษาใหม่ที่เน้น imperative (แนว Fortran) ล้วน ๆ และออกแนว minimalism คำสั่งที่มีมาให้ก็เป็นไปแต่แบบพอเพียง บางอย่างที่เขาคิดว่าไม่จำเป็นเขาก็ไม่มีให้ ทำให้แน่นอนไม่มี map, fold, foldr, filter แถมมาให้ ถ้าอยากได้ก็ต้องทำเองในระดับ library

แน่นอนว่าในระดับ Library นี้ compiler เข้ามาช่วย optimize ให้ได้ยาก พอใช้ไปก็จะทำให้โปรแกรมช้าได้

ด้วย 2 เหตุนี้คือขัดวิถีโกและช้า ทำให้โอกาสที่ใคร ๆ จะเขียนแนว Lisp/Scheme ใน Go มีน้อย

ดังนั้นใครหรือทีมไหนที่ไม่ชอบเขียนโปรแกรมที่มีกลิ่นอายของ Lisp/Scheme ในยุคนี้ภาษา Go เหมาะอย่างยิ่ง

Sometimes a consumer takes a long time to finish a task, and producers keep sending data. This situation can cause a memory leak. Keep processing out-dated data makes no sense in some applications.

So I write a program to keep receiving data within 10ms time frame, and discard old data.

use std::sync::mpsc::channel;
use std::{thread, time};

fn main() {
    let (tx, rx) = channel();
    thread::spawn(move || {
        let tx_delay_time = time::Duration::from_millis(100);
        loop {
            for i in 1..1000 {
                tx.send(i).expect("Cannot send");
                if i % 10 == 0 {
                    thread::sleep(tx_delay_time);
                }
            }
        }
    });

    let timeout = time::Duration::from_millis(10);
    loop {
        println!("LOOP");
        let mut i = None;
        loop {
            match rx.recv_timeout(timeout) {
                Ok(j) => {
                    i = Some(j);
                },
                Err(_) => {
                    if i.is_some() {
                        break;
                    }
                }
            }
        }
        // long task ...
        println!("{:?}", i);
        i = None;
    }
}

I feel that the program look too complicated. Perhaps I shouldn't a channel at all in this case. 🤣

(custom-set-faces
 ;; custom-set-faces was added by Custom.
 ;; If you edit it by hand, you could mess it up, so be careful.
 ;; Your init file should contain only one such instance.
 ;; If there is more than one, they won't work right.
 '(default ((t (:family "Noto Mono" :foundry "GOOG" :slant normal :weight normal :height 128 :width normal))))
 '(rainbow-delimiters-depth-2-face ((t (:inherit rainbow-delimiters-base-face :foreground "SeaGreen3"))))
 '(rainbow-delimiters-depth-3-face ((t (:inherit rainbow-delimiters-base-face :foreground "DeepPink1"))))
 '(rainbow-delimiters-depth-4-face ((t (:inherit rainbow-delimiters-base-face :foreground "SteelBlue1"))))
 '(rainbow-delimiters-depth-5-face ((t (:inherit rainbow-delimiters-base-face :foreground "orange red"))))
 '(rainbow-delimiters-depth-6-face ((t (:inherit rainbow-delimiters-base-face :foreground "DarkGoldenrod1")))))

In this meetup, we

  • Trained new translator by collective translation
  • Learned about roles and job description for each role
  • Learned about a code of conduct
  • Defined our responsibility assignment matrix
  • Defined our onboarding process for new contributors
  • Planned to experiment with translating in cross-checking mode
  • Updated our style guide and move it from old wiki page to GitHub and reformat it to Markdown
  • Planed about meetups and applying Mozilla products and projects in educations
  • Talked about how to improve Pontoon in the way that could help us work faster
  • Identified L10n related bugs, which crucial for Thai speakers

Wannaphong is our new translator. He works mainly on localizing Common Voice website. All Thai speakers in the meetup help him review his translations together at once, to demonstrate how to improve our translations.

We collectively reviewed the translations

Now our community has three roles, i.e., contributor, translator, and manager. In brief, everyone can translate at least by making a suggestion. A translator is supposed to work on reviewing other people's translation too. And a manager should manage to build a healthy community, and communicating between all participants.

Mozilla L10N is one of the most culturally diverse free/libre/open source software projects that coordinate contributors around the world for supporting their local languages. IMO, having a code of conduct, namely Mozilla Community Participation Guidelines is vital for making sure that the community does not discourage any demographic group of people from participating. Mozilla has simplified the Community Participation Guidelines, so it should be easier for us to practice.

We also defined our responsibility assignment matrix, and our onboarding process for new contributors, which is supposed to be published later.

For improving translation quality, we planned to experiment with translating in cross-checking mode on Common Voice project first because at least we have more than two translators working on this project. By having more than one translator, we can suggest the translation and another translator can cross-check by reviewing it. However, for trivial translations, cross-checking by making suggestion instead of translating in one step should be avoided.

We updated our style guide in Thai and put it on GitHub. To avoid editing to many suggested translations and make our translation consistent. The style guide is essential for anyone who wants to contribute.

We were updating the style guide, and defining the onboarding process

To have more participant and make more impact, we plan to invite people to talk about other topics rather than L10n in the meetup. We used to talk about Rust, and it went quite well.

About Pontoon, we agree that it is the best localization platform, which we have ever use. And it can improve even further by retrieving existing translations of terms in a long or short string that we about to translate. Moreover, enabling Pontoon to search only on Thai strings in particular patterns can detect errors, without looking at a translation unit one by one.

We believe that language features are critical for the competitiveness of Firefox and related projects among Thai language speaking users. So, we wish #1423593 and #425915 will get fixed soon.

  • Thank Opendream and @kengggg@twitter for offering the meetup venue for free.
  • Thank Wichai Termwuttipreecha for event planning.
  • Thank Peiying Mo, Delphine Lebédel, and @pompoko35@twitter, Teerapat Taechaiya for planning and organizing.
  • Thank Can Udomcharoenchaikit, Nattapong Ekudomsuk, Thanatip Sriviroonchai, Wannaphong Phatthiyaphaibun, Hassadee Pimsuwan, and Densin Roy. for contributing.

Almost everyone

Vee Satayamas