veer66

veer66

  • sudo apt-get install fonts-noto fonts-noto-extra fonts-noto-core fonts-noto-mono fonts-noto-ui-core fonts-noto-ui-extra fonts-noto-cjk fonts-noto-unhinted fonts-noto-hinted fonts-noto-cjk-extra fonts-noto-color-emoji
  • mkdir -p $HOME/.config/fontconfig
  • cat > $HOME/.config/fontconfig/fonts.conf

แปะอันนี้ใส่ไป

<?xml version='1.0'?>
<!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
<fontconfig>
  <match>
    <test compare="contains" name="lang">
      <string>th</string>
    </test>
    <edit mode="prepend" name="family">
      <string>Noto Sans Thai</string>
    </edit>
  </match>
  <alias>
    <family>serif</family>
    <prefer>
      <family>Noto Serif</family>
    </prefer>    
  </alias>
  <alias>
    <family>sans-serif</family>
    <prefer>
      <family>Noto Sans</family>
    </prefer>    
  </alias>
  <alias>
    <family>sans</family>
    <prefer>
      <family>Noto Sans</family>
    </prefer>    
  </alias>
  <alias>
    <family>monospace</family>
    <prefer>
      <family>Noto Mono</family>
    </prefer>    
  </alias>
</fontconfig>
  • กด control D

เสร็จ

บางทีเปลี่ยน font แล้วลง พวก Noto เรียนร้อยแล้วก็ยังไม่ได้อย่างใจ บางทีเป็นเพราะว่าระบบจะเลือก font ให้เราเอง ซึ่งไปแก้ได้ในไฟล์

ผมชึ้นหัวว่า MX Linux แต่จริง ๆ จะใช้ Void Linux, Ubuntu กระทั่งพวก NetBSD ก็น่าจะเหมือนกันหมด

$HOME/.config/fontconfig/fonts.conf

ซึ่งอันนี้ผมแก้ตาม https://jichu4n.com/posts/how-to-set-default-fonts-and-font-aliases-on-linux/ และ https://wiki.archlinux.org/index.php/Font_configuration/Examples

สมมุติว่าในกรณีที่ลง Noto แต่ไม่มี font ของ TLWG นะครับ ใส่ไปแบบนี้

<?xml version='1.0'?>
<!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
<fontconfig>
  <match>
    <test compare="contains" name="lang">
      <string>th</string>
    </test>
    <edit mode="prepend" name="family">
      <string>Noto Sans Thai</string>
    </edit>
  </match>
  <alias>
    <family>serif</family>
    <prefer>
      <family>Noto Serif</family>
    </prefer>    
  </alias>
  <alias>
    <family>sans-serif</family>
    <prefer>
      <family>Noto Sans</family>
    </prefer>    
  </alias>
  <alias>
    <family>sans</family>
    <prefer>
      <family>Noto Sans</family>
    </prefer>    
  </alias>
  <alias>
    <family>monospace</family>
    <prefer>
      <family>Noto Mono</family>
    </prefer>    
  </alias>
</fontconfig>

แต่ทำไปทำมาก็รู้สึกว่าไม่ค่อยโดยเท่าไหร่ ผมก็ลง Loma แล้วเปลี่ยน Noto Sans Thai เป็น Loma อยู่ดี

ชีวิตผมจะทำอะไรอีก ก็มีประมาณนี้ล่ะ

(defn iter>seq [it]
  []
  (when (.hasNext it)
    (let [p (.next it)]
      (lazy-seq (cons [(read-val* (.key p))
                       (read-val (.val p))]
                      (iter>seq it))))))

###########
# # # # # # # #
###########

ใน Python ตาม code ข้างล่าง append เป็น method ของ object a แต่พอมานับจำนวนสมาชิกใช้ len(a) ซึ่ง len เป็น built-in function แทน ถ้าอยากให้มีรูปแบบเดียวกันเปลี่ยนจาก len(a) ไปเป็น a.__len__() ก็ได้ แต่ก็จะมี ____ เกินมาอีก ใน Python เองก็คงจะมีหลักเกณฑ์ว่าเมื่อไหร่อะไรจะเป็น function อะไรจะเป็น method ซึ่งก็อาจจะต้องเรียนรู้หรือจำไว้

a = [1, 2, 3, 4, 5]
a.append(10)
print(len(a))

ใน JavaScript ก็มีรูปแบบที่ต่างกันคือ push เป็น method แต่ length เป็น property สังเกตได้จากว่าตอนเรียกไม่มีวงเล็บ ใน JavaScript ก็คงมีหลักการว่าเมื่อไหร่อะไรจะเป็น method หรือเป็น property ซึ่งก็ต้องเรียนรู้หรือจำไว้

a = [1, 2, 3, 4, 5]
a.push(10)
console.log(a.length)

ใน Ruby ทีนี้เหมือนกันแล้วคือ push ก็เป็น method และ length ก็เป็น method

a = [1, 2, 3, 4, 5]
a.push(10)
p(a.length)

แต่ก็ยังมีประเด็นต่อไปอีกว่าทำไมใน Ruby เขียนแบบนี้

10 + 20 + 30 + 40 + 50

ไม่เป็น method และเรียกเหมือนอย่างอื่นบ้างล่ะ

10.+(20).+(30).+(40).+(50)

(Emacs) Lisp จะต่างกับอันอื่นหน่อย ตรงที่ push มันไปต่อหน้าแทนที่จะต่อหลัง

(setq a '(1 2 3 4 5))
(push 10 a)
(print (length a))
(+ 10 20 30 40 50)

เขียน Lisp นี่ทุกอย่างก็จะเรียงเหมือนกันหมดเลย ได้แก่ (push ...) (length ...) (+ ...) ไม่เดี๋ยวต่อหน้า len(...) หรือเดี๋ยวต่อหลังเป็น a.length หรือว่าเดี๋ยวมีหรือไม่มีวงเล็บ เช่น a.length กับ a.length

แต่ปัญหาที่มันจะเจอคือหลายท่านรับไม่ได้ที่วงเล็บไปอยู่ข้างหน้าชื่อ function ถึงกับว่ามันวงเล็บย้ายที่แล้วเขียนไม่ได้เลย

แต่ Julia ก็เขียนแบบนี้ได้ append! ก็เป็น length ก็เป็น function ทั้งคู่ แถมตอนบวกก็เขียนแบบ function ได้ด้วย

a = [1,2,3,4,5]
append!(a,10)
print(length(a))
+(10,20,30,40,50) # จะเขียนแบบ 10 + 20 + 30 + 40 + 50 ก็ได้

แต่ก็อาจจะคิดได้ว่าทำเวลาสร้าง array ไม่เป็น function บ้าง ก็อาจจะเปลี่ยน [1,2,3,4,5] เป็น push!([],1,2,3,4,5) แทน ทีนี้ก็จะเหลือแต่ = แล้วที่ดูไม่เหมือนอย่างอื่น ซึ่งอันนี้ผมก็นึกวิธีแก้ให้มันเหมือนกันไม่ออก

from google.colab import drive
drive.mount('/content/drive')
import os
os.listdir('/content/drive/My Drive')

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

อย่างแรกเลยสมมุติว่า 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 ครับ เพื่องานวิจัยและไม่ได้มีการหาผลประโยชน์อีกเช่นเคย

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

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