2023年5月31日水曜日

日本語の大規模言語モデルllamaを動かす。

 おじさん、日本語に対応しているLLMを動かしたくていろいろ調べてみました。

https://gigazine.net/news/20230403-llama-cpp-ram/

------------------------

2023年2月にMetaが発表した大規模言語モデル「LLaMA」は、従来のGPT-3よりも小規模でありながらGPT-3に匹敵する性能を単体GPUの環境でも示すことが可能とされており、2023年3月にはエンジニアのジョージ・ゲルガノフ氏がM1などのsearch Apple シリコン搭載MacでLLaMAを動作させる「llama.cpp」を公開しました。そんな中、プログラマーのジャスティン・タニー氏がllama.cppが動作する際のメモリ使用量を減らすアップデートを行い、LLaMAの一部モデルに至っては6GB未満のRAMで動作することが報告されています。

------------------------

なるほど、llama.cppを使えばその辺のスペックのPCでChat-GPTのようなものが動くのね。

しかもたくさんの日本語のモデルが公開されているじゃないか!


https://zenn.dev/syoyo/articles/f869a3fda15f45


https://medium.com/axinc/llama-cpp%E3%81%A8lora%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%97%E3%81%A6pc%E4%B8%8A%E3%81%A7%E6%97%A5%E6%9C%AC%E8%AA%9Ellm%E3%83%A2%E3%83%87%E3%83%AB%E3%82%92%E5%AE%9F%E8%A1%8C%E3%81%99%E3%82%8B-31195fd54737


https://zenn.dev/syoyo/articles/946c17666e10fb


llama.cppはソースが毎日ものすごい変更されているようで、タグのバージョンが一致しないと公開されているモデルがデータが動かないようです。

llama.cppってllamaだけじゃなくてrinnaとかいろんなモデルも動くのね。

おじさん、「llama.cpp-master-63d2046」を取ってきて古いIntelMACでビルドして動かしてみました。




------------------------


$ ./main -m ../ggml-vic7b-q4_0.bin -p "こんにちは"

main: build = 0 (unknown)

main: seed  = 1685513870

llama.cpp: loading model from ../ggml-vic7b-q4_0.bin

llama_model_load_internal: format     = ggjt v2 (latest)

llama_model_load_internal: n_vocab    = 32000

llama_model_load_internal: n_ctx      = 512

llama_model_load_internal: n_embd     = 4096

llama_model_load_internal: n_mult     = 256

llama_model_load_internal: n_head     = 32

llama_model_load_internal: n_layer    = 32

llama_model_load_internal: n_rot      = 128

llama_model_load_internal: ftype      = 2 (mostly Q4_0)

llama_model_load_internal: n_ff       = 11008

llama_model_load_internal: n_parts    = 1

llama_model_load_internal: model size = 7B

llama_model_load_internal: ggml ctx size =  72.75 KB

llama_model_load_internal: mem required  = 5809.34 MB (+ 1026.00 MB per state)

llama_init_from_file: kv self size  =  256.00 MB


system_info: n_threads = 6 / 12 | AVX = 1 | AVX2 = 1 | AVX512 = 0 | AVX512_VBMI = 0 | AVX512_VNNI = 0 | FMA = 1 | NEON = 0 | ARM_FMA = 0 | F16C = 1 | FP16_VA = 0 | WASM_SIMD = 0 | BLAS = 1 | SSE3 = 1 | VSX = 0 | 

sampling: repeat_last_n = 64, repeat_penalty = 1.100000, presence_penalty = 0.000000, frequency_penalty = 0.000000, top_k = 40, tfs_z = 1.000000, top_p = 0.950000, typical_p = 1.000000, temp = 0.800000, mirostat = 0, mirostat_lr = 0.100000, mirostat_ent = 5.000000

generate: n_ctx = 512, n_batch = 512, n_predict = -1, n_keep = 0



 こんにちは私は東京都、渋谷区の小学生です。当初私は日本語が複雑でしたが、徐歴uraの出ましい動画を見ると私は日本語がきっかけになりました。よろしいお手? microphone(mikeroop) 読み取ればよいですね」


このように、徐歴uraの動画を見ると日本語がきっかけになり、学端を解消しました。今、学端に違反しないようにお手? Mikeroop 読み取ればよいですね。


その後、徐歴uraが日本語の練習を行っている動画を見たり、広葉大輔などから学んだことを活用しました。今、日本語で書き上げることができます。


その中でも特におそらえ難しい日本語である「拝? Her Majesty the Queen's 」という用語が、徐歴uraが紹介した動画を見て学びました。感謝し合い。 [end of text]


llama_print_timings:        load time =   767.94 ms

llama_print_timings:      sample time =   229.23 ms /   351 runs   (    0.65 ms per token)

llama_print_timings: prompt eval time =   751.15 ms /     7 tokens (  107.31 ms per token)

llama_print_timings:        eval time = 55632.42 ms /   350 runs   (  158.95 ms per token)

llama_print_timings:       total time = 56655.94 ms

yomeinoMac-mini:llama.cpp-master-63d2046 yomei$ 

----------------------------


こりゃすげーすごい速さで答えが出てきます。

おーあなた小学生なのね?

やっぱ4Gバイトの7Bのモデルだと日本語がへたくそだねー。

10Gある13Bのモデルだとこれよりきれいな日本語で答えが出るみたいなので10Gダウンロードしよう。

2023年5月30日火曜日

oneDNNを使ってみる。

 最近、大規模なディープラーニングの学習にはoneDNNが使われています。

おじさんの大好きなC++なのでさっそくビルドして使ってみました。



ディープラーニングのフレームワークっていろんなライブラリに依存しているのですが、これは他に依存ライブラリなくて単体でビルド可能です。


ビルドしてみると確かに簡単にビルドできるし、いろんなCPUやGPU、スレッドモデル、分散処理に対応しています。

だから富岳とかのスパコンでも簡単に移植して動かせるのね。


Windows用にビルドできたんだけど、おじさんスパコン持っていないので何に使おうかなぁ。


とりあえずCNNの学習サンプルは動いた。

--------------------------------

#include <assert.h>


#include <math.h>


#include "oneapi_dnnl_dnnl.hpp"


#include "example_utils.hpp"


using namespace dnnl;


void simple_net(engine::kind engine_kind) {

    using tag = memory::format_tag;

    using dt = memory::data_type;


    auto eng = engine(engine_kind, 0);

    stream s(eng);


    // Vector of primitives and their execute arguments

    std::vector<primitive> net_fwd, net_bwd;

    std::vector<std::unordered_map<int, memory>> net_fwd_args, net_bwd_args;


    const int batch = 32;


    std::vector<float> net_src(batch * 3 * 227 * 227);

    std::vector<float> net_dst(batch * 96 * 27 * 27);


    // initializing non-zero values for src

    for (size_t i = 0; i < net_src.size(); ++i)

        net_src[i] = sinf((float)i);


    // AlexNet: conv

    // {batch, 3, 227, 227} (x) {96, 3, 11, 11} -> {batch, 96, 55, 55}

    // strides: {4, 4}


    memory::dims conv_src_tz = {batch, 3, 227, 227};

    memory::dims conv_weights_tz = {96, 3, 11, 11};

    memory::dims conv_bias_tz = {96};

    memory::dims conv_dst_tz = {batch, 96, 55, 55};

    memory::dims conv_strides = {4, 4};

    memory::dims conv_padding = {0, 0};


    std::vector<float> conv_weights(product(conv_weights_tz));

    std::vector<float> conv_bias(product(conv_bias_tz));


    // initializing non-zero values for weights and bias

    for (size_t i = 0; i < conv_weights.size(); ++i)

        conv_weights[i] = sinf((float)i);

    for (size_t i = 0; i < conv_bias.size(); ++i)

        conv_bias[i] = sinf((float)i);


    // create memory for user data

    auto conv_user_src_memory

            = memory({{conv_src_tz}, dt::f32, tag::nchw}, eng);

    write_to_dnnl_memory(net_src.data(), conv_user_src_memory);

    auto conv_user_weights_memory

            = memory({{conv_weights_tz}, dt::f32, tag::oihw}, eng);

    write_to_dnnl_memory((void *)conv_weights.data(), conv_user_weights_memory);

    auto conv_user_bias_memory = memory({{conv_bias_tz}, dt::f32, tag::x}, eng);

    write_to_dnnl_memory(conv_bias.data(), conv_user_bias_memory);


    // create memory descriptors for convolution data w/ no specified

    // format tag(`any`)

    // tag `any` lets a primitive(convolution in this case)

    // chose the memory format preferred for best performance.

    auto conv_src_md = memory::desc({conv_src_tz}, dt::f32, tag::any);

    auto conv_bias_md = memory::desc({conv_bias_tz}, dt::f32, tag::any);

    auto conv_weights_md = memory::desc({conv_weights_tz}, dt::f32, tag::any);

    auto conv_dst_md = memory::desc({conv_dst_tz}, dt::f32, tag::any);


    // create a convolution primitive descriptor

    auto conv_pd = convolution_forward::primitive_desc(eng, prop_kind::forward,

            algorithm::convolution_direct, conv_src_md, conv_weights_md,

            conv_bias_md, conv_dst_md, conv_strides, conv_padding,

            conv_padding);


    // create reorder primitives between user input and conv src if needed

    auto conv_src_memory = conv_user_src_memory;

    if (conv_pd.src_desc() != conv_user_src_memory.get_desc()) {

        conv_src_memory = memory(conv_pd.src_desc(), eng);

        net_fwd.push_back(reorder(conv_user_src_memory, conv_src_memory));

        net_fwd_args.push_back({{DNNL_ARG_FROM, conv_user_src_memory},

                {DNNL_ARG_TO, conv_src_memory}});

    }


    auto conv_weights_memory = conv_user_weights_memory;

    if (conv_pd.weights_desc() != conv_user_weights_memory.get_desc()) {

        conv_weights_memory = memory(conv_pd.weights_desc(), eng);

        net_fwd.push_back(

                reorder(conv_user_weights_memory, conv_weights_memory));

        net_fwd_args.push_back({{DNNL_ARG_FROM, conv_user_weights_memory},

                {DNNL_ARG_TO, conv_weights_memory}});

    }


    // create memory for conv dst

    auto conv_dst_memory = memory(conv_pd.dst_desc(), eng);


    // finally create a convolution primitive

    net_fwd.push_back(convolution_forward(conv_pd));

    net_fwd_args.push_back({{DNNL_ARG_SRC, conv_src_memory},

            {DNNL_ARG_WEIGHTS, conv_weights_memory},

            {DNNL_ARG_BIAS, conv_user_bias_memory},

            {DNNL_ARG_DST, conv_dst_memory}});


    // AlexNet: relu

    // {batch, 96, 55, 55} -> {batch, 96, 55, 55}

    memory::dims relu_data_tz = {batch, 96, 55, 55};

    const float negative_slope = 0.0f;


    // create relu primitive desc

    // keep memory format tag of source same as the format tag of convolution

    // output in order to avoid reorder

    auto relu_pd = eltwise_forward::primitive_desc(eng, prop_kind::forward,

            algorithm::eltwise_relu, conv_pd.dst_desc(), conv_pd.dst_desc(),

            negative_slope);


    // create relu dst memory

    auto relu_dst_memory = memory(relu_pd.dst_desc(), eng);


    // finally create a relu primitive

    net_fwd.push_back(eltwise_forward(relu_pd));

    net_fwd_args.push_back(

            {{DNNL_ARG_SRC, conv_dst_memory}, {DNNL_ARG_DST, relu_dst_memory}});


    // AlexNet: lrn

    // {batch, 96, 55, 55} -> {batch, 96, 55, 55}

    // local size: 5

    // alpha: 0.0001

    // beta: 0.75

    // k: 1.0

    memory::dims lrn_data_tz = {batch, 96, 55, 55};

    const uint32_t local_size = 5;

    const float alpha = 0.0001f;

    const float beta = 0.75f;

    const float k = 1.0f;


    // create a lrn primitive descriptor

    auto lrn_pd = lrn_forward::primitive_desc(eng, prop_kind::forward,

            algorithm::lrn_across_channels, relu_pd.dst_desc(),

            relu_pd.dst_desc(), local_size, alpha, beta, k);


    // create lrn dst memory

    auto lrn_dst_memory = memory(lrn_pd.dst_desc(), eng);


    // create workspace only in training and only for forward primitive

    // query lrn_pd for workspace, this memory will be shared with forward lrn

    auto lrn_workspace_memory = memory(lrn_pd.workspace_desc(), eng);


    // finally create a lrn primitive

    net_fwd.push_back(lrn_forward(lrn_pd));

    net_fwd_args.push_back(

            {{DNNL_ARG_SRC, relu_dst_memory}, {DNNL_ARG_DST, lrn_dst_memory},

                    {DNNL_ARG_WORKSPACE, lrn_workspace_memory}});


    // AlexNet: pool

    // {batch, 96, 55, 55} -> {batch, 96, 27, 27}

    // kernel: {3, 3}

    // strides: {2, 2}


    memory::dims pool_dst_tz = {batch, 96, 27, 27};

    memory::dims pool_kernel = {3, 3};

    memory::dims pool_strides = {2, 2};

    memory::dims pool_dilation = {0, 0};

    memory::dims pool_padding = {0, 0};


    // create memory for pool dst data in user format

    auto pool_user_dst_memory

            = memory({{pool_dst_tz}, dt::f32, tag::nchw}, eng);

    write_to_dnnl_memory(net_dst.data(), pool_user_dst_memory);


    // create pool dst memory descriptor in format any

    auto pool_dst_md = memory::desc({pool_dst_tz}, dt::f32, tag::any);


    // create a pooling primitive descriptor

    auto pool_pd = pooling_forward::primitive_desc(eng, prop_kind::forward,

            algorithm::pooling_max, lrn_dst_memory.get_desc(), pool_dst_md,

            pool_strides, pool_kernel, pool_dilation, pool_padding,

            pool_padding);


    // create pooling workspace memory if training

    auto pool_workspace_memory = memory(pool_pd.workspace_desc(), eng);


    // create a pooling primitive

    net_fwd.push_back(pooling_forward(pool_pd));

    // leave DST unknown for now (see the next reorder)

    net_fwd_args.push_back({{DNNL_ARG_SRC, lrn_dst_memory},

            // delay putting DST until reorder (if needed)

            {DNNL_ARG_WORKSPACE, pool_workspace_memory}});


    // create reorder primitive between pool dst and user dst format

    // if needed

    auto pool_dst_memory = pool_user_dst_memory;

    if (pool_pd.dst_desc() != pool_user_dst_memory.get_desc()) {

        pool_dst_memory = memory(pool_pd.dst_desc(), eng);

        net_fwd_args.back().insert({DNNL_ARG_DST, pool_dst_memory});

        net_fwd.push_back(reorder(pool_dst_memory, pool_user_dst_memory));

        net_fwd_args.push_back({{DNNL_ARG_FROM, pool_dst_memory},

                {DNNL_ARG_TO, pool_user_dst_memory}});

    } else {

        net_fwd_args.back().insert({DNNL_ARG_DST, pool_dst_memory});

    }


    //-----------------------------------------------------------------------

    //----------------- Backward Stream -------------------------------------

    // ... user diff_data ...

    std::vector<float> net_diff_dst(batch * 96 * 27 * 27);

    for (size_t i = 0; i < net_diff_dst.size(); ++i)

        net_diff_dst[i] = sinf((float)i);


    // create memory for user diff dst data

    auto pool_user_diff_dst_memory

            = memory({{pool_dst_tz}, dt::f32, tag::nchw}, eng);

    write_to_dnnl_memory(net_diff_dst.data(), pool_user_diff_dst_memory);


    // Backward pooling

    // create memory descriptors for pooling

    auto pool_diff_src_md = memory::desc({lrn_data_tz}, dt::f32, tag::any);

    auto pool_diff_dst_md = memory::desc({pool_dst_tz}, dt::f32, tag::any);


    // backward primitive descriptor needs to hint forward descriptor

    auto pool_bwd_pd = pooling_backward::primitive_desc(eng,

            algorithm::pooling_max, pool_diff_src_md, pool_diff_dst_md,

            pool_strides, pool_kernel, pool_dilation, pool_padding,

            pool_padding, pool_pd);


    // create reorder primitive between user diff dst and pool diff dst

    // if required

    auto pool_diff_dst_memory = pool_user_diff_dst_memory;

    if (pool_dst_memory.get_desc() != pool_user_diff_dst_memory.get_desc()) {

        pool_diff_dst_memory = memory(pool_dst_memory.get_desc(), eng);

        net_bwd.push_back(

                reorder(pool_user_diff_dst_memory, pool_diff_dst_memory));

        net_bwd_args.push_back({{DNNL_ARG_FROM, pool_user_diff_dst_memory},

                {DNNL_ARG_TO, pool_diff_dst_memory}});

    }


    // create memory for pool diff src

    auto pool_diff_src_memory = memory(pool_bwd_pd.diff_src_desc(), eng);


    // finally create backward pooling primitive

    net_bwd.push_back(pooling_backward(pool_bwd_pd));

    net_bwd_args.push_back({{DNNL_ARG_DIFF_DST, pool_diff_dst_memory},

            {DNNL_ARG_DIFF_SRC, pool_diff_src_memory},

            {DNNL_ARG_WORKSPACE, pool_workspace_memory}});


    // Backward lrn

    auto lrn_diff_dst_md = memory::desc({lrn_data_tz}, dt::f32, tag::any);

    const auto &lrn_diff_src_md = lrn_diff_dst_md;


    // create backward lrn primitive descriptor

    auto lrn_bwd_pd = lrn_backward::primitive_desc(eng,

            algorithm::lrn_across_channels, lrn_diff_src_md, lrn_diff_dst_md,

            lrn_pd.src_desc(), local_size, alpha, beta, k, lrn_pd);


    // create reorder primitive between pool diff src and lrn diff dst

    // if required

    auto lrn_diff_dst_memory = pool_diff_src_memory;

    if (lrn_diff_dst_memory.get_desc() != lrn_bwd_pd.diff_dst_desc()) {

        lrn_diff_dst_memory = memory(lrn_bwd_pd.diff_dst_desc(), eng);

        net_bwd.push_back(reorder(pool_diff_src_memory, lrn_diff_dst_memory));

        net_bwd_args.push_back({{DNNL_ARG_FROM, pool_diff_src_memory},

                {DNNL_ARG_TO, lrn_diff_dst_memory}});

    }


    // create memory for lrn diff src

    auto lrn_diff_src_memory = memory(lrn_bwd_pd.diff_src_desc(), eng);


    // finally create a lrn backward primitive

    // backward lrn needs src: relu dst in this topology

    net_bwd.push_back(lrn_backward(lrn_bwd_pd));

    net_bwd_args.push_back({{DNNL_ARG_SRC, relu_dst_memory},

            {DNNL_ARG_DIFF_DST, lrn_diff_dst_memory},

            {DNNL_ARG_DIFF_SRC, lrn_diff_src_memory},

            {DNNL_ARG_WORKSPACE, lrn_workspace_memory}});


    // Backward relu

    auto relu_diff_src_md = memory::desc({relu_data_tz}, dt::f32, tag::any);

    auto relu_diff_dst_md = memory::desc({relu_data_tz}, dt::f32, tag::any);

    auto relu_src_md = conv_pd.dst_desc();


    // create backward relu primitive_descriptor

    auto relu_bwd_pd = eltwise_backward::primitive_desc(eng,

            algorithm::eltwise_relu, relu_diff_src_md, relu_diff_dst_md,

            relu_src_md, negative_slope, relu_pd);


    // create reorder primitive between lrn diff src and relu diff dst

    // if required

    auto relu_diff_dst_memory = lrn_diff_src_memory;

    if (relu_diff_dst_memory.get_desc() != relu_bwd_pd.diff_dst_desc()) {

        relu_diff_dst_memory = memory(relu_bwd_pd.diff_dst_desc(), eng);

        net_bwd.push_back(reorder(lrn_diff_src_memory, relu_diff_dst_memory));

        net_bwd_args.push_back({{DNNL_ARG_FROM, lrn_diff_src_memory},

                {DNNL_ARG_TO, relu_diff_dst_memory}});

    }


    // create memory for relu diff src

    auto relu_diff_src_memory = memory(relu_bwd_pd.diff_src_desc(), eng);


    // finally create a backward relu primitive

    net_bwd.push_back(eltwise_backward(relu_bwd_pd));

    net_bwd_args.push_back({{DNNL_ARG_SRC, conv_dst_memory},

            {DNNL_ARG_DIFF_DST, relu_diff_dst_memory},

            {DNNL_ARG_DIFF_SRC, relu_diff_src_memory}});


    // Backward convolution with respect to weights

    // create user format diff weights and diff bias memory

    std::vector<float> conv_user_diff_weights_buffer(product(conv_weights_tz));

    std::vector<float> conv_diff_bias_buffer(product(conv_bias_tz));


    auto conv_user_diff_weights_memory

            = memory({{conv_weights_tz}, dt::f32, tag::nchw}, eng);

    write_to_dnnl_memory(conv_user_diff_weights_buffer.data(),

            conv_user_diff_weights_memory);

    auto conv_diff_bias_memory = memory({{conv_bias_tz}, dt::f32, tag::x}, eng);

    write_to_dnnl_memory(conv_diff_bias_buffer.data(), conv_diff_bias_memory);


    // create memory descriptors

    auto conv_bwd_src_md = memory::desc({conv_src_tz}, dt::f32, tag::any);

    auto conv_diff_bias_md = memory::desc({conv_bias_tz}, dt::f32, tag::any);

    auto conv_diff_weights_md

            = memory::desc({conv_weights_tz}, dt::f32, tag::any);

    auto conv_diff_dst_md = memory::desc({conv_dst_tz}, dt::f32, tag::any);


    // create backward convolution primitive descriptor

    auto conv_bwd_weights_pd = convolution_backward_weights::primitive_desc(eng,

            algorithm::convolution_direct, conv_bwd_src_md,

            conv_diff_weights_md, conv_diff_bias_md, conv_diff_dst_md,

            conv_strides, conv_padding, conv_padding, conv_pd);


    // for best performance convolution backward might chose

    // different memory format for src and diff_dst

    // than the memory formats preferred by forward convolution

    // for src and dst respectively

    // create reorder primitives for src from forward convolution to the

    // format chosen by backward convolution

    auto conv_bwd_src_memory = conv_src_memory;

    if (conv_bwd_weights_pd.src_desc() != conv_src_memory.get_desc()) {

        conv_bwd_src_memory = memory(conv_bwd_weights_pd.src_desc(), eng);

        net_bwd.push_back(reorder(conv_src_memory, conv_bwd_src_memory));

        net_bwd_args.push_back({{DNNL_ARG_FROM, conv_src_memory},

                {DNNL_ARG_TO, conv_bwd_src_memory}});

    }


    // create reorder primitives for diff_dst between diff_src from relu_bwd

    // and format preferred by conv_diff_weights

    auto conv_diff_dst_memory = relu_diff_src_memory;

    if (conv_bwd_weights_pd.diff_dst_desc()

            != relu_diff_src_memory.get_desc()) {

        conv_diff_dst_memory = memory(conv_bwd_weights_pd.diff_dst_desc(), eng);

        net_bwd.push_back(reorder(relu_diff_src_memory, conv_diff_dst_memory));

        net_bwd_args.push_back({{DNNL_ARG_FROM, relu_diff_src_memory},

                {DNNL_ARG_TO, conv_diff_dst_memory}});

    }


    // create backward convolution primitive

    net_bwd.push_back(convolution_backward_weights(conv_bwd_weights_pd));

    net_bwd_args.push_back({{DNNL_ARG_SRC, conv_bwd_src_memory},

            {DNNL_ARG_DIFF_DST, conv_diff_dst_memory},

            // delay putting DIFF_WEIGHTS until reorder (if needed)

            {DNNL_ARG_DIFF_BIAS, conv_diff_bias_memory}});


    // create reorder primitives between conv diff weights and user diff weights

    // if needed

    auto conv_diff_weights_memory = conv_user_diff_weights_memory;

    if (conv_bwd_weights_pd.diff_weights_desc()

            != conv_user_diff_weights_memory.get_desc()) {

        conv_diff_weights_memory

                = memory(conv_bwd_weights_pd.diff_weights_desc(), eng);

        net_bwd_args.back().insert(

                {DNNL_ARG_DIFF_WEIGHTS, conv_diff_weights_memory});


        net_bwd.push_back(reorder(

                conv_diff_weights_memory, conv_user_diff_weights_memory));

        net_bwd_args.push_back({{DNNL_ARG_FROM, conv_diff_weights_memory},

                {DNNL_ARG_TO, conv_user_diff_weights_memory}});

    } else {

        net_bwd_args.back().insert(

                {DNNL_ARG_DIFF_WEIGHTS, conv_diff_weights_memory});

    }


    // didn't we forget anything?

    assert(net_fwd.size() == net_fwd_args.size() && "something is missing");

    assert(net_bwd.size() == net_bwd_args.size() && "something is missing");


    int n_iter = 1; // number of iterations for training

    // execute

    while (n_iter) {

        // forward

        for (size_t i = 0; i < net_fwd.size(); ++i)

            net_fwd.at(i).execute(s, net_fwd_args.at(i));


        // update net_diff_dst

        // auto net_output = pool_user_dst_memory.get_data_handle();

        // ..user updates net_diff_dst using net_output...

        // some user defined func update_diff_dst(net_diff_dst.data(),

        // net_output)


        for (size_t i = 0; i < net_bwd.size(); ++i)

            net_bwd.at(i).execute(s, net_bwd_args.at(i));

        // update weights and bias using diff weights and bias

        //

        // auto net_diff_weights

        //     = conv_user_diff_weights_memory.get_data_handle();

        // auto net_diff_bias = conv_diff_bias_memory.get_data_handle();

        //

        // ...user updates weights and bias using diff weights and bias...

        //

        // some user defined func update_weights(conv_weights.data(),

        // conv_bias.data(), net_diff_weights, net_diff_bias);


        --n_iter;

    }


    s.wait();

}


int main(int argc, char **argv) {

    return handle_example_errors(simple_net, parse_engine_kind(argc, argv));

}

--------------------------------




2023年5月24日水曜日

GPTでPytorchを使ったニュートン法をやってみる

おじさん、最近、自動微分とか自動ベクトル化の勉強にはまっています。

JAXとかfunctorchとかものすごい勢いで開発されてるし。

ここでLLMが500億円で計算できるか50億円で計算できるか決まるしね。

だけどドキュメントがまったくない・・・・・。

--------

Why functorch?

This library is currently under heavy development - if you have suggestions on the API or use-cases you'd like to be covered, please open an github issue or reach out. We'd love to hear about how you're using the library.

functorch is JAX-like composable function transforms for PyTorch.

--------

このへん、ちょー開発中なのね。

おじさん、自動微分ってディープラーニングだけじゃなくて、制御や物理シミュレーションや数値計算、株価予想とかいろいろ面白い使い方があると思っているのですが、自動微分を使ったニュートン法とかの基礎的なサンプルコードがないからまだ誰も使ってないと思っています。

株価の予想に自動微分使って一儲けできないかなぁ。とか書くとめちゃくちゃ怪しいメールがたくさん来ます。


おじさんChatGPTに、PytorchのC++APIを使ってニュートン法を自動微分でするサンプルをお願いしてみました。



けど、最新の部分ってChatGPTもわからない・・・。というかChatGPTって誰かが書いたコードをコピペしかできないでしょ。動かない適当なコードばっかりはくし、オリジナルのコード全くかけない。


しょーがないなー。

というわけで、まだ世界でだれも作っていない、バックプロパゲーションの基礎の基礎であるニュートン法をC++でPytorchの自動微分機能を使ってするサンプルを自分で書いてみました。

----------------------------------

 #include <torch_torch.h>


torch::Tensor f(const torch::Tensor& x_tensor)

{

torch::Tensor y = x_tensor * x_tensor - 2;

return y;

}


float newtonMethod(float x) {

torch::Tensor x_tensor = torch::tensor(x, torch::requires_grad());

torch::Tensor y;


for (int i = 0; i < 5; ++i) {

y = f(x_tensor);

y.backward();


torch::Tensor grad = x_tensor.grad();


x_tensor.data() -= (y / grad).data();

x_tensor.grad().zero_();

std::cout << x_tensor.item<float>() << std::endl;

}

return x_tensor.item<float>();

}


int main() {

float initial_guess = 5.0;

float result = newtonMethod(initial_guess);

std::cout << "Approximate solution: " << result << std::endl;

return 0;

}

----------------------------------

oneDnn+flashlightも使ってみたいのでいろいろ調べているのですが、どのライブラリもAPI似てますね。


結果

----------------------------------

2.7

1.72037

1.44146

1.41447

1.41421

Approximate solution: 1.41421

----------------------------------

できた!

これを変数でなくて複雑なベクトル関数でやるとバックプロパゲーションになるのね。



2023年5月10日水曜日

C++でトップダウン型自動微分をする

C++で書かれたAIの学習プログラムが少ない理由に、トップダウン型の自動微分ができないからというのがあります。

自動微分ができないと、バックプロパゲーションするときに偏微分値を手動で計算してさらにそれをコーディングしないと求められなくて、学習やレイヤー設計が簡単にできないようです。

この問題クリアしないと、組み込みやスパコンでC++で学習させたりできないじゃん。

みんなどうやって自動微分しているのか調べてみました。



・FB


https://pytorch.org/tutorials/advanced/cpp_autograd.html

PytorchってC++で自動微分ができるんですね。すげー。

Pytorchは自動ベクトル化もできるのでC++とかもでもきるんだろうか?


・Google

TensorFlow2じゃないと自動微分できないし、PythonのautogradやJAXライブラ使っているのでC++で自動微分できません。力わざとお金の力で解決?

なんとGoogleが負けている。なんという過酷な開発競争


・PFN

https://www.preferred.jp/ja/news/pr20181203-1/

PFNもC++で自動微分するの大変だったのね。

でも撤退。県外。


・OpenAI


https://github.com/oneapi-src/oneDNN

OneDNNというC++自動微分できるライブラリを自分たちで作っている!

そりゃそうだよね。そうしないとChatGPTとか計算できないよね。



色々調べてみると、どうやらFBのPytorchとOpenAIのOneDNNくらいしかC++で自動微分できないのね。

しかもカスタムのベクトル関数をC++で自動微分できるのはPytorchしかない・・・

悲しい。


ボトムアップ型の自動微分ならば二重数で計算するだけなんですが、トップダウン型のベクトルの自動微分ってそんなに実装するの大変なの?


自動微分をC++で簡単に使うことができる様にしました。

https://github.com/takahiro1127/AutomaticDifferentiation


トップダウン型の自動微分を実装する

https://olanleed.hatenablog.com/entry/2023/04/16/203423


自動微分(Automatic differentiation)のSurvey論文の参照

https://lib-arts.hatenablog.com/entry/diff_implement2



どうやらスカラー型の自動微分のライブラリしか簡単に試せる実装はないらしいです。

任意のベクトル関数の自動微分をするってけっこう大変なのね。

勉強になるねぇ。

自動微分できないと、自動ベクトル化や最適化とかもできなさそうで、負の連鎖になるように思うんですが、どうなんでしょう?


ということで、誰も日本人でC++でベクトルのトップダウン型自動微分をやっている人がいないので、以前Windowsでカスタムビルドできるようにした、pytorchのC++で自動微分してみました。


----------------------

#include <iostream>

#include <torch_torch.h>



int main()

{


auto model = torch::nn::Linear(4, 3);


auto input = torch::randn({ 3, 4 }).requires_grad_(true);

auto output = model(input);


// Calculate loss

auto target = torch::randn({ 3, 3 });

auto loss = torch::nn::MSELoss()(output, target);


// Use norm of gradients as penalty

auto grad_output = torch::ones_like(output);

auto gradient = torch::autograd::grad({ output }, { input }, /*grad_outputs=*/{ grad_output }, /*create_graph=*/true)[0];

auto gradient_penalty = torch::pow((gradient.norm(2, /*dim=*/1) - 1), 2).mean();


// Add gradient penalty to loss

auto combined_loss = loss + gradient_penalty;

combined_loss.backward();


std::cout << input.grad() << std::endl;


return 0;

}



----------------------



----------------------

-0.0678 -0.1252  0.0566  0.0149

 0.2211 -0.0657  0.0009 -0.0186

-0.0982  0.0493  0.0301  0.0358

[ CPUFloatType{3,4} ]

----------------------


できた。

これでおじさんもC++でディープラーニングのレイヤ設計やモデル設計ができるようになりました。


2023年3月15日水曜日

GPTを動かす

 なんか最近GPTが流行っているようです。

おじさんも自分のマシンでGPTを動かしたくなってきました。



ということで、C++で動くGPUがいらないGPTを探してみました。

https://github.com/ggerganov/ggml/tree/master/examples/gpt-j

GPTってソースとってくれば10分くらいでマシンが作れるんですね。

これ本当に動くの?

スクラッチで書かれていて、*.cのファイル3個しかないし。

フル版だと72Gバイトのメモリと学習済みデータがいるらしい。

スモール版だと12Gバイトで動くらしい。

学習済みデータも置いてあるし。なんと親切な。

ちょーお手軽じゃん。


とりあえずAndroid版とM1-Mac版とIntel-Mac版とVisualC++版がビルドできた。


-----------------------

yomeinoMac-mini:jni yomei$ 

yomeinoMac-mini:jni yomei$ ./test -m ggml-model-gpt-j-6B.bin -p "Who are you ?"

main: seed = 1678930370

gptj_model_load: loading model from 'ggml-model-gpt-j-6B.bin' - please wait ...

gptj_model_load: n_vocab = 50400

gptj_model_load: n_ctx   = 2048

gptj_model_load: n_embd  = 4096

gptj_model_load: n_head  = 16

gptj_model_load: n_layer = 28

gptj_model_load: n_rot   = 64

gptj_model_load: f16     = 1

gptj_model_load: ggml ctx size = 13334.86 MB

gptj_model_load: memory_size =  1792.00 MB, n_mem = 57344

gptj_model_load: ................................... done

gptj_model_load: model size = 11542.79 MB / num tensors = 285

main: number of tokens in prompt = 4


Who are you ?

I am a professional web developer who lives on the north of England. I have an affinity for Linux, Open Source Software, and general geekery. I am currently learning Ruby


....

-----------------------

Intel Macで動かしたら、すげー動く!。

僕のマシンだと解答が出始めるのに10分くらいかかります。

あんた、Rubyを勉強しているね。偉いねー。


行列の掛け算を数十回するだけでなぜか会話ができてしまう。

これ手軽にデバッグできるから、AttentionとかTransformerの動きを勉強するのに最適だね。



2023年1月9日月曜日

中学校の入試問題をプログラムで解く。

 あけましておめでとうございます。ことしもよろしくお願いします。

おじさん、毎年この時期に新聞とかに載っている入試問題をといています。

暇つぶしに解いていたら、なんと中学入試の問題にプログラムコンテストの問題が混じっているではないか!




https://www10.schoolweb.ne.jp/weblog/files/1320077/doc/86294/1393171.pdf

このPDFの10ページの[2]の問2。


おじさん、年とともにどんどん問題を解くのに時間がかかり、解けなくなっていきます。かなしい。

この問題、たぶん3分くらいでとけないといけないのですが、24時間以上間考えてもわからない。


おじさん忠実に問題を解いていますが、この問題、相当難しい。

問題を複雑にしているポイントは3つ。



一つ目は、赤色+赤色の定義。


「赤色+赤色」を「赤色+赤色=赤色」とするのか、「赤色+赤色=2×赤色」とするのか演算処理が定義がされていない。

加法混色で赤色と青色と緑色がそれぞれ個数に関係なく兎に角3種類あれば白色なのか。

赤色と青色と緑色が同じ数だけある場合のみが白色なのか。

別の言い方をすると、「赤色+赤色+緑色+青色」を白色と扱うかピンクっぽい色って扱うか。


二つ目は、使って良いかい中電灯の個数。

かい中電灯を何個使っていいか問題には書かれていない。

仮にかい中電灯を2個だけでなく3個以上使って良く、かつ、「赤色+赤色=赤色」を定義する場合、結構な数の組み合わせになる。

ためしに答案用紙の広さを見ると一行分しかないじゃない。

超ちっちゃく書かないと答案用紙にはいらない!


三つ目は灰色を使うかどうか。

「赤色+緑色+青色」を白色と定義した場合、「2×赤色+2×緑色+2×青色」という白色を超える白色のパターンがでてしまう。これを白色とするのか、それとも「赤色+緑色+青色」を灰色として「2×赤色+2×緑色+2×青色」のみを白色とするのか。

白色と黒色と灰色って定義が難しい。



この問題、そもそも一つ目の問題を後者として問題を解くと答えの総数が減るんですが、灰色問題が出てきてしまって問題がとけない。一つ目の問題を前者として問題を解くと答えの総数が多くて大変なことになります。



なので、ありそうな答えの込み合わせをすべてプログラムで組んでみました。


----------------------------

#include <stdio.h>

#include <string.h>


#define R 1

#define G 10

#define B 100


int kaicyu[] = {

    R,G,R + G, R+B,G+B

};

const char* name[] = {

    "赤色","緑色","黄色","赤むらさき色","空色"

};


#define isR(a) (a%10)

#define isG(a) ((a/10)%10)

#define isB(a) ((a/100)%10)


#define syokusu(a) (((a&1)!=0)+((a&2)!=0)+((a&4)!=0)+((a&8)!=0)+((a&16)!=0))



int main()

{

    int i, c, m, j;

    char buf[256];


    printf("ptn1\n");

    printf("----------\n");

    for (i = 1; i <= 31; i++)

    {

        buf[0] = 0;

        c = 0;

        for (m = 1, j = 0; j < 5; j++, m <<= 1) {

            if (i & m) {

                c += kaicyu[j];

                if (buf[0] != 0)strcat(buf, "と");

                strcat(buf, name[j]);

            }

        }

        if (isR(c) && isG(c) && isB(c)) {

            printf("%s\n", buf);

        }

    }

    printf("\n");


    printf("ptn2\n");

    printf("----------\n");

    for (i = 1; i <= 31; i++)

    {

        buf[0] = 0;

        c = 0;

        for (m = 1, j = 0; j < 5; j++, m <<= 1) {

            if (i & m) {

                c += kaicyu[j];

                if (buf[0] != 0)strcat(buf, "と");

                strcat(buf, name[j]);

            }

        }

        if (isR(c) == isG(c) && isR(c) == isB(c)) {

            printf("%s\n", buf);

        }

    }

    printf("\n");


    printf("ptn3\n");

    printf("----------\n");

    for (i = 1; i <= 31; i++)

    {

        buf[0] = 0;

        c = 0;

        for (m = 1, j = 0; j < 5; j++, m <<= 1) {

            if (i & m) {

                c += kaicyu[j];

                if (buf[0] != 0)strcat(buf, "と");

                strcat(buf, name[j]);

            }

        }

        if (isR(c) == 2 && isG(c) == 2 && isB(c) == 2) {

            printf("%s\n", buf);

        }

    }

    printf("\n");


    printf("ptn4\n");

    printf("----------\n");

    for (i = 1; i <= 31; i++)

    {

        buf[0] = 0;

        c = 0;

        for (m = 1, j = 0; j < 5; j++, m <<= 1) {

            if (i & m) {

                c += kaicyu[j];

                if (buf[0] != 0)strcat(buf, "と");

                strcat(buf, name[j]);

            }

        }

        if (isR(c) == 1 && isG(c) == 1 && isB(c) == 1) {

            printf("%s\n", buf);

        }

    }

    printf("\n");


    printf("ptn5\n");

    printf("----------\n");

    for (i = 1; i <= 31; i++)

    {

        buf[0] = 0;

        c = 0;

        for (m = 1, j = 0; j < 5; j++, m <<= 1) {

            if (i & m) {

                c += kaicyu[j];

                if (buf[0] != 0)strcat(buf, "と");

                strcat(buf, name[j]);

            }

        }

        if (syokusu(i)==2 && isR(c) && isG(c) && isB(c) ) {

            printf("%s\n", buf);

        }

    }

    printf("\n");


    return 0;

}

----------------------------



----------------------------

ptn1

----------

緑色と赤むらさき色

赤色と緑色と赤むらさき色

黄色と赤むらさき色

赤色と黄色と赤むらさき色

緑色と黄色と赤むらさき色

赤色と緑色と黄色と赤むらさき色

赤色と空色

赤色と緑色と空色

黄色と空色

赤色と黄色と空色

緑色と黄色と空色

赤色と緑色と黄色と空色

赤むらさき色と空色

赤色と赤むらさき色と空色

緑色と赤むらさき色と空色

赤色と緑色と赤むらさき色と空色

黄色と赤むらさき色と空色

赤色と黄色と赤むらさき色と空色

緑色と黄色と赤むらさき色と空色

赤色と緑色と黄色と赤むらさき色と空色


ptn2

----------

緑色と赤むらさき色

赤色と空色

赤色と緑色と赤むらさき色と空色

黄色と赤むらさき色と空色


ptn3

----------

赤色と緑色と赤むらさき色と空色

黄色と赤むらさき色と空色


ptn4

----------

緑色と赤むらさき色

赤色と空色


ptn5

----------

緑色と赤むらさき色

黄色と赤むらさき色

赤色と空色

黄色と空色

赤むらさき色と空色

----------------------------


中学校の入学試験ってとっても難しい。

うーん、これ本当にとけた小学生いるのだろうか・・・。

どれが正解なんだ???。

SAPIXの先生か誰か答えを教えてください。

2022年12月30日金曜日

libusbをなおす。

 USBのデバイスを制御するライブラリにlibusbというものがあります。

このライブラリを使うと、PCやスマホのUSBのカメラから動画をキャプチャーしたり、iphoneを制御したりできます。


AIの画像認識や組み込みとかでとっても使えるこのライブラリですが、バグでWindowsのコンポジットデバイスのアイソクロナス転送が動かない。

これだとUSBカメラとかUSBオーディオがおじさんの大好きなWindowsで使えないじゃないか。

そしてこのバグを誰も修正してくれない。


https://github.com/libuvc/libuvc/issues/12

このバグ、7年間も放置されています。

世界に80億人もいるのに。

もう誰もWindowsなんか使ってないのね。

最近いろいろなオープンソースでWindowsのバグが修正されていなくて困ります。


誰も修正してくれないので、おじさんが二週間ソースコードを解析してオープンソースを修正してみました。


ソースコードを直すときのメモリリークチェックとかリソースハンドルのクローズ忘れとかのチェックが大変ですね。

だけどとりあえずできた!

ライセンスがLGPLなので、ソースコードを修正すると公開しないといけないです。

https://github.com/yomei-o/libusb-1

これでWindowsでlibusbとlibuvcを使ってUSBカメラを制御できる!

おじさんもちょっと世の中の役に立ったかな。