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++でディープラーニングのレイヤ設計やモデル設計ができるようになりました。