ネコでもつくれる人工知能

日記です。1日やったことを書いていきます。内容はそんなにレベルが高くないものになると思います。

beautifulsoupかゆいところに手が届くリスト

tl;dr

覚書

python3.6
beautifulsoup4==4.7.1

タグ取り出し

  • find_all()は独自型じゃなくてlistで返す
  • タグでfindしたあとそのタグ自体を消したいときは.string
    (ex:
    検索元タグあり = soup.find('p')
    検索元タグあり = soup.find('p').string

    bs4.element.Tag型が持ってる属性だからfind_all()で取得したものに全部適応するときはmap()とかワンテンポ置く必要ある

逐一追記する

GPUを無料で使う方法の模索

無料でGPUを使う方法の模索

高すぎ 論外
でもAWSの他のアプリつかうなら一番良い
オススメ度☆

  • GCE tensorflow-gpu

無料クレジットがあるもののクレカ登録必須
tensorflow前提なのが悲しい
でもAPIが充実してるし速い(らしい)
オススメ度☆☆☆

  • リアルでどこかから拝借してくる

泥棒はよくない
もらえるならもらおう
オススメ度☆☆

  • kaggleのkernelを使う

いくらつかっても無料だぞ
でもwebアプリだぞ
あとseleniumとかで無理やりkaggle以外の機械学習用途に使うのは規約違反だぞ
オススメ度☆☆☆☆

おわり

増えたら書く

LINEBot&Clova Advent Calendar 2018(12/20)

こんにちは。
近頃めちゃめちゃ寒いですね〜〜

アドベントカレンダーも今日で20日目です!
私は正直最近LINEbotに全く触れておらずそれ以外のことでてんてこ舞いです。なので、やってる分野とうまく知見を共有できないかなと思い、私が少しプレゼンした際の機械学習の資料を貼ろうと思います。

docs.google.com

これは半教師あり学習というMLの一つで、前半は説明要素が多いですが後半にテーマである「bot開発で役に立つAPIOSSの紹介、かんたんな非公式APIの作り方」がちょくちょく出てきます!!

解説を逐一増やしていこうと思います。
わからない点があればお気軽にどうぞ!
質が低いのはご了承ください笑

ではまた追記します!(日付まにあったかな…??)

【OS X】wgetコマンドが使えない際の解決方法(2019)

osxはhomebrewがあります。

$ brew install wget

で一発!!
というわけには行かないことが多いです

$ wget -h

で動作確認が行えますが

dyld: Library not loaded: /usr/local/opt/gettext/lib/libintl.8.dylib
  Referenced from: /usr/local/bin/wget
  Reason: image not found
Abort trap: 6

怒られます。

これはOSXでデフォで入っているgettextライブラリの問題です。 PATHを弄るより、homebrewに頼る方が安全です。

$ brew uninstall --force gettext
$ brew install gettext

gettext is keg-only, which means it was not symlinked into /usr/local,
because macOS provides the BSD gettext library & some software gets confused if both are in the library path.

If you need to have gettext first in your PATH run:
  echo 'export PATH="/usr/local/opt/gettext/bin:$PATH"' >> ~/.bash_profile

For compilers to find gettext you may need to set:
  export LDFLAGS="-L/usr/local/opt/gettext/lib"
  export CPPFLAGS="-I/usr/local/opt/gettext/include"

PATHをイジってもいいですがちゃんと$ source ~/.${シェルのプロファイル}をしましょう
gettextのインスコが終わったら

$ brew uninstall --force wget
$ brew install wget
$ wget -h
GNU Wget 1.19.5, 非対話的ネットワーク転送ソフト
使い方: wget [オプション]... [URL]...

長いオプションで不可欠な引数は短いオプションでも不可欠です。

スタートアップ:
  -V,  --version                   バージョン情報を表示して終了する
  -h,  --help                      このヘルプを表示する
  -b,  --background                スタート後にバックグラウンドに移行する
  -e,  --execute=COMMAND           `.wgetrc'形式のコマンドを実行する

ログと入力ファイル:
  -o,  --output-file=FILE          ログを FILE に出力する
  -a,  --append-output=FILE        メッセージを FILE に追記する
  -q,  --quiet                     何も出力しない
  -v,  --verbose                   冗長な出力をする (デフォルト)
  -nv, --no-verbose                冗長ではなくする
       --report-speed=TYPE         帯域幅を TYPE で出力します。TYPE は 'bits' が指定できます。
  -i,  --input-file=FILE           FILE の中に指定された URL をダウンロードする
  -F,  --force-html                入力ファイルを HTML として扱う
  -B,  --base=URL                  HTML で入力されたファイル(-i -F)のリンクを
                                   指定した URL の相対 URL として扱う
       --config=FILE               設定ファイルを指定する
       --no-config                 設定ファイルを読みこまない
       --rejected-log=FILE         拒否された理由をログ FILE に保存する

ダウンロード:
  -t,  --tries=NUMBER              リトライ回数の上限を指定 (0 は無制限).
       --retry-connrefused         接続を拒否されてもリトライする
       --retry-on-http-error=ERRORS    comma-separated list of HTTP errors to retry
  -O,  --output-document=FILE      FILE に文書を書きこむ
  -nc, --no-clobber                存在しているファイルをダウンロードで上書きしない
       --no-netrc                  don't try to obtain credentials from .netrc
  -c,  --continue                  部分的にダウンロードしたファイルの続きから始める
       --start-pos=OFFSET          OFFSET からダウンロードを開始する
       --progress=TYPE             進行表示ゲージの種類を TYPE に指定する
       --show-progress             どのモードでも進捗バーを表示する
  -N,  --timestamping              ローカルにあるファイルよりも新しいファイルだけ取得する
       --no-if-modified-since      タイムスタンプモードの時に、
                                     if-modified-since get リクエストを使わない
  --no-use-server-timestamps       ローカル側のファイルのタイムスタンプに
                                   サーバのものを使わない
  -S,  --server-response           サーバの応答を表示する
       --spider                    何もダウンロードしない
  -T,  --timeout=SECONDS           全てのタイムアウトを SECONDS 秒に設定する
       --dns-timeout=SECS          DNS 問い合わせのタイムアウトを SECS 秒に設定する
       --connect-timeout=SECS      接続タイムアウトを SECS 秒に設定する
       --read-timeout=SECS         読み込みタイムアウトを SECS 秒に設定する
  -w,  --wait=SECONDS              ダウンロード毎に SECONDS 秒待つ
       --waitretry=SECONDS         リトライ毎に 1〜SECONDS 秒待つ
       --random-wait               ダウンロード毎に 0.5*WAIT〜1.5*WAIT 秒待つ
       --no-proxy                  プロクシを使わない
  -Q,  --quota=NUMBER              ダウンロードするバイト数の上限を指定する
       --bind-address=ADDRESS      ローカルアドレスとして ADDRESS (ホスト名か IP) を使う
       --limit-rate=RATE           ダウンロード速度を RATE に制限する
       --no-dns-cache              DNS の問い合わせ結果をキャッシュしない
       --restrict-file-names=OS    OS が許しているファイル名に制限する
       --ignore-case               ファイル名/ディレクトリ名の比較で大文字小文字を無視する
  -4,  --inet4-only                IPv4 だけを使う
  -6,  --inet6-only                IPv6 だけを使う
       --prefer-family=FAMILY      指定したファミリ(IPv6, IPv4, none)で最初に接続する
       --user=USER                 ftp, http のユーザ名を指定する
       --password=PASS             ftp, http のパスワードを指定する
       --ask-password              パスワードを別途入力する
       --use-askpass=COMMAND       認証情報(ユーザ名とパスワード)を取得するハンドラを指定します。
                                     COMMAND が指定されない場合は、
                                     環境変数 WGET_ASKPASS か SSH_ASKPASS が
                                     使われます。
       --no-iri                    IRI サポートを使わない
       --local-encoding=ENC        指定した ENC を IRI のローカルエンコーディングにする
       --remote-encoding=ENC       指定した ENC をデフォルトのリモートエンコーディングにする
       --unlink                    上書きする前にファイルを削除する
       --no-xattr                  拡張ファイル属性へのメタデータ保存を無効にする

ディレクトリ:
  -nd, --no-directories            ディレクトリを作らない
  -x,  --force-directories         ディレクトリを強制的に作る
  -nH, --no-host-directories       ホスト名のディレクトリを作らない
       --protocol-directories      プロトコル名のディレクトリを作る
  -P,  --directory-prefix=PREFIX   ファイルを PREFIX/ 以下に保存する
       --cut-dirs=NUMBER           リモートディレクトリ名の NUMBER 階層分を無視する

HTTP オプション:
       --http-user=USER            http ユーザ名として USER を使う
       --http-password=PASS        http パスワードとして PASS を使う
       --no-cache                  サーバがキャッシュしたデータを許可しない
       --default-page=NAME         デフォルトのページ名を NAME に変更します
                                   通常は `index.html' です
  -E,  --adjust-extension          HTML/CSS 文書は適切な拡張子で保存する
       --ignore-length             `Content-Length' ヘッダを無視する
       --header=STRING             送信するヘッダに STRING を追加する
       --compression=TYPE          choose compression, one of auto, gzip and none. (default: none)
       --max-redirect              ページで許可する最大転送回数
       --proxy-user=USER           プロクシユーザ名として USER を使う
       --proxy-password=PASS       プロクシパスワードとして PASS を使う
       --referer=URL               Referer を URL に設定する
       --save-headers              HTTP のヘッダをファイルに保存する
  -U,  --user-agent=AGENT          User-Agent として Wget/VERSION ではなく AGENT を使う
       --no-http-keep-alive        HTTP の keep-alive (持続的接続) 機能を使わない
       --no-cookies                クッキーを使わない
       --load-cookies=FILE         クッキーを FILE から読みこむ
       --save-cookies=FILE         クッキーを FILE に保存する
       --keep-session-cookies      セッションだけで用いるクッキーを保持する
       --post-data=STRING          POST メソッドを用いて STRING を送信する
       --post-file=FILE            POST メソッドを用いて FILE の中味を送信する
       --method=HTTPMethod         "HTTPMethod" をヘッダのメソッドとして使います
       --body-data=STRING          STRING をデータとして送る。--method を指定してください。
       --body-file=FILE            ファイルの中味を送る。--method を指定してください。
       --content-disposition       Content-Disposition ヘッダがあれば
                                   ローカルのファイル名として用いる (実験的)
       --content-on-error          サーバエラー時に受信した内容を出力する
       --auth-no-challenge         サーバからのチャレンジを待たずに、
                                   Basic認証の情報を送信します。

HTTPS (SSL/TLS) オプション:
       --secure-protocol=PR        choose secure protocol, one of auto, SSLv2,
                                     SSLv3, TLSv1, TLSv1_1, TLSv1_2 and PFS
       --https-only                安全な HTTPS のリンクだけたどる
       --no-check-certificate      サーバ証明書を検証しない
       --certificate=FILE          クライアント証明書として FILE を使う
       --certificate-type=TYPE     クライアント証明書の種類を TYPE (PEM, DER) に設定する
       --private-key=FILE          秘密鍵として FILE を使う
       --private-key-type=TYPE     秘密鍵の種類を TYPE (PEM, DER) に設定する
       --ca-certificate=FILE       CA 証明書として FILE を使う
       --ca-directory=DIR          CA のハッシュリストが保持されているディレクトリを指定する
       --crl-file=FILE             CRL ファイルを指定する
       --pinnedpubkey=FILE/HASHES  公開鍵 (PEM/DER) ファイル、もしくは、base64でエンコードした
                                   sha256ハッシュ値(sha256//で始まりセミコロン区切り)を指定して、
                                   相手を認証します。
       --random-file=FILE          SSL PRNG の初期化データに使うファイルを指定する
       --egd-file=FILE             EGD ソケットとして FILE を使う

       --ciphers=STR           Set the priority string (GnuTLS) or cipher list string (OpenSSL) directly.
                                   Use with care. This option overrides --secure-protocol.
                                   The format and syntax of this string depend on the specific SSL/TLS engine.
HSTS オプション:
       --no-hsts                   HSTS を使わない
       --hsts-file                 HSTS データベースのパス (デフォルトを上書き)

FTP オプション:
       --ftp-user=USER             ftp ユーザとして USER を使う
       --ftp-password=PASS         ftp パスワードとして PASS を使う
       --no-remove-listing         `.listing' ファイルを削除しない
       --no-glob                   FTP ファイル名のグロブを無効にする
       --no-passive-ftp            "passive" 転送モードを使わない
       --preserve-permissions      リモートのファイルパーミッションを保存する
       --retr-symlinks             再帰取得中に、シンボリックリンクでリンクされた先のファイルを取得する

FTPS オプション:
       --ftps-implicit                 implicit FTPS を使う (デフォルトポートは 990)
       --ftps-resume-ssl               制御接続で開始した SSL/TLS セッションを
                                         データ接続で再開する
       --ftps-clear-data-connection    制御チャネルだけ暗号化する(データは平文になる)
       --ftps-fallback-to-ftp          サーバが FTPS に対応していない場合は FTP にする
WARC オプション:
       --warc-file=FILENAME        リクエスト/レスポンスデータを .warc.gz ファイルに保存する
       --warc-header=STRING        warcinfo record に STRING を追加する
       --warc-max-size=NUMBER      WARC ファイルのサイズの最大値を NUMBER に設定する
       --warc-cdx                  CDX インデックスファイルを書く
       --warc-dedup=FILENAME       指定した CDX ファイルに載っている record は保存しない
       --no-warc-compression       WARC ファイルを GZIP で圧縮しない
       --no-warc-digests           SHA1 ダイジェストを計算しない
       --no-warc-keep-log          WARC record にログファイルを保存しない
       --warc-tempdir=DIRECTORY    WARC 書込時の一時ファイルを置くディレクトリを指定する

再帰ダウンロード:
  -r,  --recursive                 再帰ダウンロードを行う
  -l,  --level=NUMBER              再帰時の階層の最大の深さを NUMBER に設定する (0 で無制限)
       --delete-after              ダウンロード終了後、ダウンロードしたファイルを削除する
  -k,  --convert-links             HTML や CSS 中のリンクをローカルを指すように変更する
       --convert-file-only         URLのファイル名部分だけ変換する (いわゆるbasename)
  --backups=N                      ファイルに書きこむ時に N ファイルのバックアップをローテーションさせる
  -K,  --backup-converted          リンク変換前のファイルを .orig として保存する
  -m,  --mirror                    -N -r -l 0 --no-remove-listing の省略形
  -p,  --page-requisites           HTML を表示するのに必要な全ての画像等も取得する
       --strict-comments           HTML 中のコメントの処理を厳密にする

再帰ダウンロード時のフィルタ:
  -A,  --accept=LIST               ダウンロードする拡張子をコンマ区切りで指定する
  -R,  --reject=LIST               ダウンロードしない拡張子をコンマ区切りで指定する
       --accept-regex=REGEX        許容する URL の正規表現を指定する
       --reject-regex=REGEX        拒否する URL の正規表現を指定する
       --regex-type=TYPE           正規表現のタイプ (posix)
  -D,  --domains=LIST              ダウンロードするドメインをコンマ区切りで指定する
       --exclude-domains=LIST      ダウンロードしないドメインをコンマ区切りで指定する
       --follow-ftp                HTML 文書中の FTP リンクも取得対象にする
       --follow-tags=LIST          取得対象にするタグ名をコンマ区切りで指定する
       --ignore-tags=LIST          取得対象にしないタグ名をコンマ区切りで指定する
  -H,  --span-hosts                再帰中に別のホストもダウンロード対象にする
  -L,  --relative                  相対リンクだけ取得対象にする
  -I,  --include-directories=LIST  取得対象にするディレクトリを指定する
  --trust-server-names             ファイル名としてリダイレクト先のURLの最後の部分を使う
  -X,  --exclude-directories=LIST  取得対象にしないディレクトリを指定する
  -np, --no-parent                 親ディレクトリを取得対象にしない

Email bug reports, questions, discussions to <bug-wget@gnu.org>
and/or open issues at https://savannah.gnu.org/bugs/?func=additem&group=wget.

やったぜ!!

おわり

【深層学習】データセットを渡すとフルオートで対立的画像を生成してラベルと一緒に返すプログラム

f:id:pppparfait:20181030220050p:plain

対立的画像生成アルゴリズムyamlから指定すると全自動で行ってくれます。

import numpy as np
import glob
import sys
import os
from keras.preprocessing.image import load_img, img_to_array, array_to_img, save_img , ImageDataGenerator
from keras.utils import to_categorical

# Generate conflicting images fully automatically :)

# [usage]
# 2 step (Please set param.yaml in advance.)
#
# instance = OpponentImage(Kernel):
# instance.return_datafile()
#
# too easy ! :)

class Kernel():

    def __init__(self):
        datas_path = '../../data/img/'
        datas_dir = os.listdir(path=datas_path)
        if '.DS_Store' in datas_dir:
            datas_dir.remove('.DS_Store')
        try:
            self.labels = list(map(lambda label: label.split('_')[0], datas_dir))
            self.params = self.read_yaml('../../param.yml')
            self.datas = glob.glob(datas_path + datas_dir[0] + '/*.{}'.format(self.params['crawler']['ext']))
            self.datas.sort()
        except Exception as e:
            sys.stderr.write(str(e))
            print()
            exit()

    def data_split(self, validation=False):
        test_num = int(len(self.datas) * self.params['ml']['test_data_rate'])
        train_num = len(self.datas) - test_num
        self.x_train_raw = self.datas[:train_num]
        self.x_test_raw = self.datas[-test_num:]

    def data_preprocess_basic(self, gray=True, size=(100,100), precision=np.float16):
        self.x_train = []
        self.y_train = []
        self.x_test = []
        self.y_test = []
        size = [self.params['ml']['img_size_xy']]*2 if not self.params['ml']['img_size_xy'] == None else size
        for label, valid in enumerate([self.x_train_raw, self.x_test_raw]):
            for img_path in valid:
                try:
                    img = load_img(img_path, grayscale=gray, target_size=tuple(size))
                    img_bin = img_to_array(img)
                    # data_container[label].append(img_bin)
                    if label == 0:
                        self.x_train.append(img_bin)
                        self.y_train.append([0])
                    elif label == 1:
                        self.x_test.append(img_bin)
                        self.y_test.append([0])
                except:
                    print('cant preprocessed image.[{}]'.format(img_path))
                    continue
                else:
                    print('encoded img.[{}]'.format(img_path))
            # self.x_train = list(map(lambda img_bin: np.float16(img_bin)/255, self.x_train))
            # self.x_test = list(map(lambda img_bin: np.float16(img_bin) / 255, self.x_test))
            self.x_train = list(map(lambda img_bin: np.ravel(precision(img_bin) / 255), self.x_train))
            self.x_test = list(map(lambda img_bin: np.ravel(precision(img_bin) / 255), self.x_test))
        print('data shape {}'.format(self.x_train[0].shape))
        print('train {}  test {}'.format(len(self.x_train), len(self.x_test)))

    def labeling(self, one_hot=True):
        pass

    def read_yaml(self, uri):
        import yaml
        try:
            with open(uri, 'r') as d:
                param_dict = yaml.load(d)
        except Exception as err:
            sys.stdout.write(str(err))
            return
        return param_dict


from PIL import Image, ImageChops, ImageOps, ImageDraw
#generate image of opponent
class OpponentImage(Kernel):

    def __init__(self):
        Kernel.__init__(self)
        self.data_split()
        self.data_preprocess_basic()
        self.ancestors = [self.x_train, self.x_test]
        self.ancestors_label = [self.y_train, self.y_test]
        self.decay = self.params['oppoimg']['decay']
        self.mode = self.params['oppoimg']['mode']
        # self.__gc_superclassvals()
        self.datas = self.make_fuzzyimg(decay=self.decay, effect=self.mode)

    def make_fuzzyimg(self, decay, effect):
        import effect_func as ef

        e_dict = {
            's_random': lambda x: ef.simple_random(x),
            'swap': lambda x: ef.swap(x),
            'as_random': lambda  x: ef.ancestral_scale_random(x),
            'as_randomv2': lambda x: ef.ancestral_scale_random_v2(x)
        }

        for i, img_bins in enumerate(self.ancestors):
            flat_img_bins = [*map(lambda img_bin: np.ravel(img_bin), img_bins)]
            for img_count, flat_img_bin in enumerate(flat_img_bins):
                # print(flat_img_bin, flat_img_bin.shape)
                # break
                try:
                    effected_bin = e_dict[effect](flat_img_bin)
                    # self.ancestors[i].append(effected_bin)
                    # self.ancestors_label[i].append([1])
                    if i == 0:
                        self.x_train.append(effected_bin)
                        self.y_train.append([1])
                    elif i == 1:
                        self.x_test.append(effected_bin)
                        self.y_test.append([1])
                except:
                    print('cant generate fuzzyimg.')
                    continue
                else:
                    print('generated fuzzyimg.{}'.format(img_count+1))
        print('fuzzy mode {}'.format(effect))
        # self.__test_show(self.ancestors[0][150])

    def anal_ances(self):
        pass

    def return_datafiles(self):
        self.y_train = to_categorical(self.y_train, num_classes=2)
        self.y_test = to_categorical(self.y_test, num_classes=2)
        return (self.x_train, self.x_test, self.y_train, self.x_test)


    def __test_show(self, np_img):
        import matplotlib.pyplot as plt
        ex_img = array_to_img(np_img.reshape(100,100,1))
        ex_img.save('test.png')

    def __gc_superclassvals(self):
        import gc
        rm_ivals = [ival for ival in list(self.__dict__.keys()) if ival is not 'ancestors']
        for rm_ival in rm_ivals:
            try:
                del rm_ival
            except Exception as e:
                print(e)
                exit()


# kernel test
if __name__ == '__main__':
    pass
    # k = Kernel()
    # k.data_split()
    # k.data_preprocess_basic()
    # oppi = OpponentImage()
    # datasets = oppi.return_datafiles()
# 対立的画像生成アルゴリズム群(模索中)
def simple_random(img_bin):
    import random
    import numpy as np
    # maxis = []
    # minis = []
    # li = img_bin.tolist()
    # li_proto = li[:]
    # random.shuffle(li)
    # print(li)
    # re_bin = np.array(li)
    # for tensor in range(len(img_bin)-1):
    #     maxis.append(img_bin[tensor].argmax())
    #     minis.append(img_bin[tensor].argmin())
    # for tensor in range(len(img_bin)):
    #     img_bin[tensor] = random.random(img_bin[tensor])
    # print(img_bin[0])
    # print(type(img_bin))
    # print(maxis)
    # print(minis)
    print(img_bin)
    re_bin = np.random.permutation(img_bin)
    print(re_bin)
    return re_bin

def ancestral_scale_random(img_bin):
    pass

def ancestral_scale_random_v2(img_bin):
    pass

def swap(img_bin):
    pass

これはカーネルファイルなので使いやすいAPIを前面に設置し、カスタマイズ性を向上させています。

f:id:pppparfait:20181030220940p:plain

全体としては各アプリケーションを独立させ、カーネルの上にAPIを設置し、そのAPIを積み木のように簡単に実行ファイルに組み立て、誰でも簡単に扱える設計となっております。

おわりに

形になったらESSLの全体アーキテクチャを公開したいです。

2値分類における完全片ラベル教師のみで分類機を作る方法の考察 あとインターンとか今後の人生

何がしたいか

現在私はESSLというweb技術を用いたワード一つから簡単に分類機を作成できる半教師あり学習の手法を提案し、開発に従事しています。

2値分類とは

私はそれを「一つの物体」「それ以外全て」だと認識しています。
「それ以外全て」を自動生成するのが非常に困難で、あらゆるアルゴリズムを模索中です。幸いCNNはNNが画像のどこを注視しているか取り出すことができるので(近年だとGrad-CAMという手法が盛んです)それを用いて取り組んだりはたまた人間では識別不能なランダムな画像生成を行なっています。
教師あり学習および深層学習のハードルを下げ浸透することを願って開発を試みています。

最後に

私は大学院生ですが、正直本来の実験よりも開発が好きです。
OSSとか大好き人間で、人の役に立った時が何より嬉しいです。
前述のESSLもきちんとアーキテクチャを整備しOSSとして公開しています(まだ開発段階ですが)

github.com

そのため修士号を取得後、民間企業に就職をすると決めています。
webと深層学習、画像処理の技術があるのが強みだと思っています。 インターンなどを通して、就職活動を頑張りたい。
おわり

機械学習アプリケーションのレビューを受ける方法

ML特に深層学習についてはまだ世界的に確立されたコンテンツは少なく、あらゆる手法が試される中、kaggleなどを見ると年々新たな知見が生まれていることがわかります。
そんな深層学習界隈は、確立されたアルゴリズムの世界の様に正しいかそうでないかの判断が難しく、(数字が出てすらいても疑う余地はある)第三者からレビューを受けることは少し難しいです。
批評を得る方法を列挙します。
下に行くほど酷いです。

素人が有識者からレビューを得る方法

  • 身近にいる人に聞く

これは情報系の大学に属している、もしくはML部門のある企業に属していないと難しいです。
しかしながら直接コミュニケーションを取れることもあり、もっとも新発見に近づくと考えています。

  • 外部勉強会などで、勇気を持ってプレゼンする

勉強会はモチベーションの塊です。その空間の面積に対する知識力やポテンシャルの比エネルギーは凄まじいでしょう。
外部勉強会などは周りの方がとても優秀に見えて萎縮してしまい、交流や議論を躊躇ってしまいがちです。
しかし私の経験上それは一部の猛者を除いてほぼ全員が感じていることです。
一人が勇気を持って意見を出すことで、周りの方も連鎖的に意見をくれます。これは美味いです。 内容レベルがどれだけ低くても後からいくらでも勉強できるのでOKだと考えています。
実際聞いていて技術的にレベルが低いと感じるものも笑ったり貶したりする気持ちにはなりません。
要するに、「レベルが低い発表をする」という自身に来る羞恥に対するプライドがあるかないかです。
私はあんまりないので優秀なエンジニアの集まりの中でも初歩的な質問などを繰り返していました。
これは私の性格で、当然プライドがあることも素晴らしいと考えています。モチベーションの源泉となり、質の良いソフトウェアなどを生み出した知人を何人も知っています。私にはできないことです。
話が逸れましたがとにかく公の場で公開してみるといいです。
リポジトリとしてプログラムを残してあると、共感してくださった方がforkして強化してくれたりメールをくれたりします。

  • 騒ぎまくる

普通に宣伝するといった範疇を超え、自演、複数アカウントなどインターネットでできる限りのことを駆使し自身のプロダクトを広めてもらいます。
これを自動化しようとするとスキルがついて美味しい。
それが炎上だろうがなんだろうが知ってもらえれば勝ちだと思います。
あるOS会社のプロダクトの初心者向け勉強会では、一人のアホ登壇者がその会社のエバンジェリストの女性を頂点とし、その場に居るであろう通常ユーザーを「一般ピープル」と位置付けヒエラルキーを持ったピラミッドを構築しました。
これはユーザーは不快に感じて当然でしょう。このあとMS社の人はケーキがどうとか登壇者のスライドをあらかじめ確認してなかっただか言ってましたが、無事叩かれていました。
しかしこれによりXamarinは広まったはずです。
こんな感じで自ら騒ぎを起こしましょう。
ネット上だけならマッチポンプである必要はなく、マッチだけで良いです。燃えれば燃えるほど美味しいです。

  • OpenAIにメールを送る

OpenAIはメンバー募集の際、履歴書と共にgithubやMLアプリケーションの概要を要求します。
これにダメ元で応募すると、なんと細かなレビューがもらえるのです。
落ちても構いません。普通は落ちます。バシバシ送りましょう。