dak ブログ

python、rubyなどのプログラミング、MySQL、サーバーの設定などの備忘録。レゴの写真も。

Node.js での map() による Array 処理

2022-02-26 23:28:42 | Node.js
Node.js で map() で Array 処理を行う方法のメモ。
const str_objs = [{str: 'abc'}, {str: 'def'}, {str: 'ghi'}];
const strs = str_objs.map((x) => { return x.str; });
console.log(strs);

■実行結果
[ 'abc', 'def', 'ghi' ]


PIL のインストール

2022-02-26 22:46:49 | python
PIL のインストール方法のメモ。

PIL をインストールするには jpeg 関連のライブラリが必要になります。
他にも環境によっては zlib-devel や python-devel が必要な場合もあります。
sudo yum install libjpeg-turbo-devel
sudo yum install zlib-devel
sudo yum install python36-devel
sudo pip install pillow



CentOS8 のデスクトップを Xfce に変更

2022-02-26 19:33:44 | linux
CentOS8 のデスクトップに GNOME を利用していましたが、
メモリ使用量が多かったため、軽量と言われている Xfce に変更してみました。
sudo yum install -y epel-release
sudo yum groupinstall -y Xfce

再起動後に「サインイン」の横の歯車から「Xfce セッション」を選択してログインすると
Xfce でログインできます。
GNOME 利用時よりも空きメモリの量が増えていました。

PIL でバイトデータから画像を生成

2022-02-23 23:15:17 | python
PIL でバイトデータから画像を生成する方法のメモ。

以下では、画像をバイトデータとしてダウンロードして、バイトデータから PIL の Image オブジェクトを生成しています。
バイトデータから Image オブジェクトを生成する際には、BytesIO を使用します。
import requests
from PIL import Image
from io import BytesIO

img_url = 'https://i.xgoo.jp/img/static/global/cmm/sn/logo_gooblog.png'
uto=format&w=384'

res = requests.get(img_url)
inst = BytesIO(res.content)
img_obj = Image.open(inst).convert('RGB')
inst.close()

img_obj.save('img.png')


GoogLeNet で出力層の手前の層を特徴ベクトルとして取得

2022-02-20 23:29:55 | 画像処理
GoogLeNet で出力層の手前の層を特徴ベクトルとして取得する方法のメモ。
register_forward_hook() で出力層の手前の層の出力を取得します。
# -*- coding:utf-8 -*-

import sys
import json
import torch
import torchvision.transforms as transforms
from PIL import Image
from googlenet_pytorch import GoogLeNet

feature_vector = None

def get_feature_vector(preproc, model, img_file):
    input_image = Image.open(img_file)
    input_tensor = preproc(input_image)
    input_batch = input_tensor.unsqueeze(0)
    
    logits = model(input_batch)
    preds = torch.topk(logits, k=5).indices.squeeze(0).tolist()
    return feature_vector

def forward_hook(module, inputs, outputs):
    global feature_vector
    feature_vector = outputs.detach().clone()[0].tolist()
    
def init():
    preproc = transforms.Compose([
        transforms.Resize(256),
        transforms.CenterCrop(224),
        transforms.ToTensor(),
        transforms.Normalize(mean=[0.485, 0.456, 0.406],
                             std=[0.229, 0.224, 0.225]),
    ])

    labels_map = json.load(open("labels_map.txt"))
    labels_map = [labels_map[str(i)] for i in range(1000)]

    model = GoogLeNet.from_pretrained("googlenet")
    model.eval()

    layers = list(model.children())
    handle = layers[-2].register_forward_hook(forward_hook)
    return preproc, model

def main():
    preproc, model = init()
    img_file = sys.argv[1]
    fv = get_feature_vector(preproc, model, img_file)
    print(fv)

    return 0

if __name__ == '__main__':
    res = main()
    exit(res)


CentOS8 で yum install でのエラー対処

2022-02-20 15:45:59 | linux
CentOS8 で yum install がエラーになり、
mirrorlist の URL を mirrorlist.centos.org から vault.centos.org に
変更すればよいという情報もありましたが、エラーが解消されないため、
以下の設定を行いました。

■/etc/yum.repos.d/CentOS-Linux-AppStream.repo
[appstream]
name=CentOS Linux $releasever - AppStream
baseurl=http://linuxsoft.cern.ch/centos-vault/8.4.2105/AppStream/$basearch/os/
#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=AppStream&infra=$infra
#baseurl=http://mirror.centos.org/$contentdir/$releasever/AppStream/$basearch/os/
gpgcheck=1
enabled=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial

baseurl 内のバージョン番号部分に $reeasever を記述すると、バージョンが 8 になってしまい、
エラーが続いたため、/etc/redhat-release に記述されているバージョン番号を指定しました。

/etc/yum.repos.d/ 内の他のファイルについても同様に URL を変更することで、
エラーが解消されました。

python で base64 で文字列をエンコード・デコード

2022-02-19 20:21:40 | python
python で base64 で文字列をエンコード・デコードする方法のメモ。
import base64

data = 'あいうえお'
data_enc = data.encode('utf-8')
data_b64 = base64.b64encode(data_enc)
print(data_b64)

data_b64_dec = base64.b64decode(data_b64)
data_dec = data_b64_dec.decode('utf-8')
print(data_dec)

■実行結果
b'44GC44GE44GG44GI44GK'
あいうえお


CRF の algorithm を変更して名詞句の判定精度を比較

2022-02-19 12:59:25 | 自然言語処理
先日の「CRF で名詞句の判定をしてみた」で、
CRF の alogorithm を変更した場合の精度を比較してみました。

変更箇所は sklearn_crfsuite.CRF() のパラメータのみです。
今回つかったデータでは algorithm=lbfgs、l2sgd、ap、pa で同程度、
algorithm=arow が精度が低いという結果になりました。
■algorithm=lbfgs, c1=0.1, c2=0.1
    crf = sklearn_crfsuite.CRF(
        algorithm='lbfgs',
        c1=0.1,
        c2=0.1,
        max_iterations=100,
        all_possible_transitions=True
    )

0.9441077916505949

■algorithm=lbfgs, c1=0.5, c2=0.5
    crf = sklearn_crfsuite.CRF(
        algorithm='lbfgs',
        c1=0.5,
        c2=0.5,
        max_iterations=100,
        all_possible_transitions=True
    )

0.945220761760303

■algorithm=l2sgd, c2=0.1
    crf = sklearn_crfsuite.CRF(
        algorithm='l2sgd',
        c2=0.1,
        max_iterations=100,
        all_possible_transitions=True
    )

0.9426298984809508

■algorithm=l2sgd, c2=0.5
    crf = sklearn_crfsuite.CRF(
        algorithm='l2sgd',
        c2=0.5,
        max_iterations=100,
        all_possible_transitions=True
    )

0.9449389892604881

■algorithm=ap
    crf = sklearn_crfsuite.CRF(
        algorithm='ap',
        max_iterations=100,
        all_possible_transitions=True
    )

0.9418937778719487

■algorithm=pa
    crf = sklearn_crfsuite.CRF(
        algorithm='pa',
        max_iterations=100,
        all_possible_transitions=True
    )

0.9407907098905913

■algorithm=arow
    crf = sklearn_crfsuite.CRF(
        algorithm='arow',
        max_iterations=100,
        all_possible_transitions=True
    )

0.8690576483344261

CRF での名詞句の判定で特徴量を変更してみた

2022-02-17 23:53:55 | 自然言語処理
前回の「CRF で名詞句の判定をしてみた」の改良版として、
前後の単語情報を特徴量に追加してみました。

変更箇所は単語の特徴量を返却するメソッドのみです。
# 単語の特徴量
def word_featureh(sent, i):
    word = sent[i]
    tkn = word[0]
    pos = word[1]

    if i > 0:
        word_p1 = sent[i-1]
        tkn_p1 = word_p1[0]
        pos_p1 = word_p1[1]
    else:
        tkn_p1 = ''
        pos_p1 = ''

    if i >= len(sent)-1:
        tkn_n1 = '<e>'
        pos_n1 = '<e>'
    else:
        word_n1 = sent[i+1]
        tkn_n1 = word_n1[0]
        pos_n1 = word_n1[1]

    feath = {
        'bias': 1.0,
        'token': tkn.lower(),
        'token.isuppser()': tkn.isupper(),
        'token.istitle()': tkn.istitle(),
        'token.isdigit()': tkn.isdigit(),
        'pos': pos,

        'token_p1': tkn_p1.lower(),
        'token_p1.isuppser()': tkn_p1.isupper(),
        'token_p1.istitle()': tkn_p1.istitle(),
        'token_p1.isdigit()': tkn_p1.isdigit(),
        'pos_p1': pos_p1,

        'token_n1': tkn_n1.lower(),
        'token_n1.isuppser()': tkn_n1.isupper(),
        'token_n1.istitle()': tkn_n1.istitle(),
        'token_n1.isdigit()': tkn_n1.isdigit(),
        'pos_n1': pos_n1,
    }

    return feath

■実行結果
0.9618864951695193

前後の単語情報を含まない場合は 0.94 程度でしたので、前後の単語情報を追加することで精度が上がっていることがわかります。

CRF で名詞句の判定を試してみた

2022-02-16 23:49:49 | 自然言語処理
conll2000 のデータで、CRF で名詞句の判定をやってみたメモ。

データは以下のように [(表記, 品詞, 名詞句ラベル), ...] の形式です。
[('Confidence', 'NN', 'B-NP'), ('in', 'IN', 'O'), ('the', 'DT', 'B-NP'), ('pound', 'NN', 'I-NP'), ('is', 'VBZ', 'O'), ('widely', 'RB', 'O'), ('expected', 'VBN', 'O'), ('to', 'TO', 'O'), ('take', 'VB', 'O'), ('another', 'DT', 'B-NP'), ('sharp', 'JJ', 'I-NP'), ('dive', 'NN', 'I-NP'), ('if', 'IN', 'O'), ('trade', 'NN', 'B-NP'), ('figures', 'NNS', 'I-NP'), ('for', 'IN', 'O'), ('September', 'NNP', 'B-NP'), (',', ',', 'O'), ('due', 'JJ', 'O'), ('for', 'IN', 'O'), ('release', 'NN', 'B-NP'), ('tomorrow', 'NN', 'B-NP'), (',', ',', 'O'), ('fail', 'VB', 'O'), ('to', 'TO', 'O'), ('show', 'VB', 'O'), ('a', 'DT', 'B-NP'), ('substantial', 'JJ', 'I-NP'), ('improvement', 'NN', 'I-NP'), ('from', 'IN', 'O'), ('July', 'NNP', 'B-NP'), ('and', 'CC', 'I-NP'), ('August', 'NNP', 'I-NP'), ("'s", 'POS', 'B-NP'), ('near-record', 'JJ', 'I-NP'), ('deficits', 'NNS', 'I-NP'), ('.', '.', 'O')]

学習データ(train.txt)で学習を行い、テストデータ(test.txt)で精度を評価します。
プログラムは以下の通り。
import nltk
from nltk.corpus import conll2000
import sklearn
import sklearn_crfsuite
from sklearn_crfsuite import metrics

# [(token, pos, label), ...]
def create_data(file):
    sents = conll2000.chunked_sents(file, chunk_types=['NP'])
    sents = [nltk.chunk.tree2conlltags(s) for s in sents]
    return sents

# 単語の特徴量
def word_featureh(sent, i):
    word = sent[i]
    tkn = word[0]
    pos = word[1]

    feath = {
        'bias': 1.0,
        'token': tkn.lower(),
        'token.isuppser()': tkn.isupper(),
        'token.istitle()': tkn.istitle(),
        'token.isdigit()': tkn.isdigit(),
        'pos': pos,
    }

    return feath

# 文の特徴量
def sent_features(sent):
    return [word_featureh(sent, i) for i in range(len(sent))]

# 文のラベル
def sent_labels(sent):
    return [word[2] for word in sent]

def main():
    train_sents = create_data('train.txt')
    X_train = [sent_features(s) for s in train_sents]
    y_train = [sent_labels(s) for s in train_sents]

    test_sents = create_data('test.txt')
    X_test =  [sent_features(s) for s in test_sents]
    y_test =  [sent_labels(s) for s in test_sents]

    crf = sklearn_crfsuite.CRF(
        algorithm='lbfgs',
        c1=0.1,
        c2=0.1,
        max_iterations=100,
        all_possible_transitions=True
    )
    crf.fit(X_train, y_train)

    labels = list(crf.classes_)
    labels.remove('O')

    y_pred = crf.predict(X_test)
    res = metrics.flat_f1_score(y_test, y_pred,
                                average='weighted', labels=labels)
    print(res)

    return 0

if __name__ == '__main__':
    res = main()
    exit(res)

実行結果は以下の通り。
0.9441077916505949

単純な特徴量でそれなりの結果がでています。

python lex-yacc で正規表現風の文法を解析

2022-02-06 20:27:06 | python
python lex-yacc で以下のような正規表現風の文法を受け付けられるパーザーを作成します。
abc
(abc|def)
a(b|c)(d|e)f
ただし、(...) の中に (...) は書けないものとします。

lex で使うシンボルは以下の通りです。
CHAR: [^|()] ※|、(、) 以外の文字。
LB:   (
RB:   )
PIPE: |

■test_lex.py
import sys
import ply.lex as lex

tokens = (
    'CHAR',
    'LB',
    'RB',
    'PIPE',
)

t_CHAR = r'[^|()]'
t_LB = r'[(]'
t_RB = r'[)]'
t_PIPE = r'[|]'

def t_error(t):
    sys.stderr.write('illegal char "%s"' % t.value[0])
    t.lexer.sip(t)

lexer = lex.lex()

yacc の文法は以下の通りです。
pttrn       : sub_pttrns
sub_pttrns  : sub_pttrn
            | sub_pttrns sub_pttrn
sub_pttrn   : chars
            | select
select      : LB chars RB
            | LB chars select_iter RB
select_iter : PIPE chars
            | PIPE chars select_iter
chars       : CHAR
            | chars CHAR

■test_yacc.py
以下で p[0] が上記の文法の左辺、p[1:] が右辺です。
import sys
import ply.yacc as yacc
from test_lex import tokens

def p_pttrn(p):
    '''pttrn : sub_pttrns'''
    p[0] = p[1]

def p_sub_pttrns(p):
    '''sub_pttrns : sub_pttrn
                  | sub_pttrns sub_pttrn'''
    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = p[1]
        p[0].extend(p[2])

def p_sub_pttrn(p):
    '''sub_pttrn : chars
                 | select'''
    p[0] = p[1]

def p_select_pttrn(p):
    '''select : LB chars RB
              | LB chars select_iter RB'''
    if len(p) == 4:
        p[0] = [{'or': [p[2]]}]
    else:
        p[0] = [p[2]]
        p[0].extend(p[3])
        p[0] = [{'or': p[0]}]

def p_select_iter(p):
    '''select_iter : PIPE chars
                   | PIPE chars select_iter'''
    if len(p) == 3:
        p[0] = p[2]
    else:
        p[0] = [p[2]]
        p[0].extend(p[3])

def p_chars(p):
    '''chars : CHAR
             | chars CHAR'''
    if len(p) == 2:
        p[0] = [p[1]]
    else:
        p[0] = p[1]
        p[0].append(p[2])

上記の文法で以下の文字列を受け付けるか解析してみます。
最後の 'a(b|(c|d))' は (...) がネストしているため、
文法には合わない文字列です。
pttrns = [
    'abc',
    '(abc)',
    '(abc|def)',
    'abc(def|ghi)(jkl|mno)pqr',
    'a(b|(c|d))',
]

parser = yacc.yacc()
for pttrn in pttrns:
    print('====')
    print('pttrn: %s' % (pttrn))
    result = parser.parse(pttrn)
    print('parsed: %s' % (result))

■実行結果
====
pttrn: abc
parsed: ['a', 'b', 'c']
====
pttrn: (abc)
parsed: [{'or': [['a', 'b', 'c']]}]
====
pttrn: (abc|def)
parsed: [{'or': [['a', 'b', 'c'], 'd', 'e', 'f']}]
====
pttrn: abc(def|ghi)(jkl|mno)pqr
parsed: ['a', 'b', 'c', {'or': [['d', 'e', 'f'], 'g', 'h', 'i']}, {'or': [['j', 'k', 'l'], 'm', 'n', 'o']}, 'p', 'q', 'r']
====
pttrn: a(b|(c|d))
parsed: None

文法通りであれば文字列を解析した結果が返却されますが、
最後の文字列は解析結果が None となっていて、文法通りではない文字列であると判定されています。