• TensorFlow2.0式の低レベルな書き方

    2020-01-24 02:26
    世界中で最も使われていて最も現場で使われているTensorFlow(以下TF)が去年、機能拡張を含んだ大型バージョンアップを行いました。その際にAPIの大掃除が行われTF1.x系列で使っていた低レベルな書き方が通用しなくなりました

    この記事は低レベルでゴリゴリ色んな所をカスタマイズして書きたいって人を支援できたら幸いです(自分自身もよくわかってない点もある)

    1.前話

    ・TF2.xで生き残っているTF1.xの書き方
    TF1.xとTF2.x両方に通用する書き方はKerasのFunctional APIとSequential APIです。
    それ以外の書き方は消えました。というかTensorFlow自体が「Kerasをベースに書いてね」という感じです。tf.layersとかは消されて使えません
    また、TF1.xからTF2.xに更新した際、まともに変更せずに動くのはKerasのみ(カスタムレイヤーとか使わずに)を使って書かれたコードだとおもいます(変換プログラムもあるけど使えるのかね)
    ※TF1.xから更新してTF2.xのKerasを動かす場合、多分Kerasのインポートを以下の様に変更する必要があります。

    from tensorfrow import keras

    ・どうしてそんなひどい事をしたの?
    答えは単純です
    「PyTorchとかの方がPythonic(Pythonぽくて)ぽくって、書きやすいから」です
    あとPyTorchの追い上げがすごいってのもある

    (引用:https://trends.google.co.jp/trends/explore?date=today%205-y&q=TensorFlow,PyTorch,Keras,Chainer)

    あとPyTorchとかKerasはオブジェクト指向ぽくかけて、TF1.xは関数型(tf.layersとかもろ関数型)ぽいからってのもあります。またはDefine on Run(TensorFlow)とDefine by Run(PyTorch)の違いもあったりします
    Define on RunとDefine by Runの解説はこの記事ではしませんが、気になる方は調べていただければ

    「Kerasでオブジェクト指向的なモデルの書き方は無いんじゃない?」って感のいい読者なら気づいたかもしれません。確かにTF1.xのKerasにはオブジェクト指向的な書き方は存在しません、TF2.xのKerasにはオブジェクト指向的なモデルの書き方ができます。本記事はその点にも触れます


    2.本編

    ・TensorFlow2.0の書き方
    TensorFlow2.0はKerasの作者によるColab Notebookによると以下の書き方があります。
    この記事では最も左下の書き方を解説します。


    ・Fully flexible with TensorFlow2.0(最も低レベルな書き方)
    TT2.xで最も低レベルな書き方は以下になります
     ・レイヤー :tf.keras.layers.layerを継承したクラスで書く
     ・レイヤーブロック :tf.keras.layers.layerかtf.keras.Modelを継承したクラスで書く
     ・モデル :tf.keras.Modelを継承したクラスで書く
     ・訓練イテレーション:クラスを宣言してその中で回す
    一番上以外はちょっとピンとこないと思います。一個ずつ解説します

    ・カスタムレイヤー :tf.keras.layers.layerを継承したクラスで書く
    TF1.xのカスタムレイヤーの書き方がそのまま使えます。
    基本的に __iniit(self)__で必要な変数を宣言してbuild(self,input_shape)で使うレイヤーと重み、バイアスを宣言します。以下例
    (引用と参考:https://www.tensorflow.org/tutorials/customization/custom_layers)
    class MyLayer(tf.keras.layers.layer):
       def __iniit__(self, output, **kwargs):
         super(MyLayer, self).__init__(**kwargs)
         self.output = output
       def build(self, input_shape):
         self.kernel = self
    .add_variable("kernel",shape=[   
                        int(input_shape[-1]),
                        self.num_outputs])
       def __call__(self, inputs):
         return tf.matmul(inputs, self.kernel)
    レイヤーブロック:tf.keras.layers.layerかtf.keras.Modelを継承したクラスで書く
    CNNとかで使われている層を重ねたブロック。上のカスタムレイヤーも入れて書くことも可能
    tf.keras.layers.layerで書く場合は重み、バイアスの宣言は行わないようにする
    tf.keras.Modelでも書ける(モデルの入れ子ができるため)けど入力の型が取れないから正直微妙である。以下例
    class MyLayerBlock_layer(tf.keras.layers.layer):
       def __iniit__(self, filter, kernel_size, channel_axes=-1 **kwargs):
         super(MyLayerBlock_layer, self).__init__(**kwargs)
         self.filter = filter
         self.kernel_size = kernel_size
         self.channel_axes = channel_axes 
       def build(self, input_shape):
         input_filter = input_shape[self.channel_axes].value
         self.conv2_a = tf.keras.layers.Conv2D(input_filter, (1, 1))
         self.bn2_a = tf.keras.layers.BatchNormalization()
         self.depthconv2 = tf.keras.layers.DepthwiseConv2D(input_filter,
                                 self.kernel_size,
                                 padding='same')
         self.bn2_depth = tf.keras.layers.BatchNormalization()
         self.conv2_b = tf.keras.layers.Conv2D(self.filter, (1, 1))
         self.bn2_b = tf.keras.layers.BatchNormalization()
       def __call__(self, inputs, training=True):
         x = self.conv2_a(input_tensor)
         x = self.bn2a(x, training=training)
         x = tf.nn.relu(x)
         x = self.depthconv2(x)
         x = self.bn2_depth(x, training=training)
         x = tf.nn.relu(x)
         x = self.conv2_b(x)
         x = self.bn2_b(x, training=training)
         x += inputs
         return tf.nn.relu(x)

    class MyLayerBlock_Model(tf.keras.Model):
       def __iniit__(self,
              input_filter,
              filter
    ,
              kernel_size,
              **kwargs):
         super(MyLayerBlock_Model, self).__init__(**kwargs)
         self.conv2_a = tf.keras.layers.Conv2D(input_filter, (1, 1))
         self.bn2_a = tf.keras.layers.BatchNormalization()
         self.depthconv2 = tf.keras.layers.DepthwiseConv2D(input_filter,
                                   kernel_size,
                                 padding='same')
         self.bn2_depth = tf.keras.layers.BatchNormalization()
         self.conv2_b = tf.keras.layers.Conv2D(filter, (1, 1))
         self.bn2_b = tf.keras.layers.BatchNormalization()
       def __call__(self, inputs, training=True):
         x = self.conv2_a(input_tensor)
         x = self.bn2a(x, training=training)
         x = tf.nn.relu(x)
         x = self.depthconv2(x)
         x = self.bn2_depth(x, training=training)
         x = tf.nn.relu(x)
         x = self.conv2_b(x)
         x = self.bn2_b(x, training=training)
         x += inputs
         return tf.nn.relu(x)
    モデル:tf.keras.Modelを継承したクラスで書く
    Kerasのベースモデルクラスを使ってモデルを作ります。これでPyTorchっぽく書けてPythonicなモデルになります(オブジェクト指向的)
    このモデルに対してcompileやpredictなどのメソッドを使うことができるが、作成したモデルを使ってtf.keras.models系の関数は使えません
    このモデルに対して複数の入力を与える場合、それぞれの1軸をバッチ次元にしたリストを渡して__call__の時に分解します
    class MyModel(tf.keras.Model):
       def __iniit__(self,output,**kwargs):
         super(MyModel, self).__init__(**kwargs)
         self.dense = tf.keras.layers.Dense(output)
       def __call__(self, inputs):
         x = self.dense(inputs)
         return tf.nn.relu(x)
    ・訓練イテレーション:クラスを宣言してその中で回す
    これが一番の肝です。以下のようにタスククラスを宣言しモデルの初期化などを行った後訓練メソッドを呼び出すようにします
    以下の場合だとtrainメソッドを呼び出せば訓練が開始されます。
    何故、こんな面倒くさい事をやってるかというと@tf.functionの自動グラフ構築を必要な所で使うことによって最適かつ高速に訓練を行う事ができます(GoogleのTransformerでも使われてるテクニック)
    Define on RunとDefine by Runを同時に操作できる強みがある
    class MyTask(object):
       def __iniit__(self,model,optimizer,loss_func,acc_func):
         self.model = model
         self.optimizer = optimizer
         self.loss_func = loss_func
         self.acc_func = acc_func

         self.model.compile() #必要ならコンパイルしとく
       def train(self, epoch):
         for iter in range(epoch):
           x, y = (データの取得用の関数とかクラス)
           loss=train_step( x, y )
         @tf.function
         def train_step(self, x, y)
           with tf.GradientTape() as tape:
              pred = self.model(x, training=True)
              loss = self.loss_func(y, pred)
           grd = tape.gradient(loss, self.model.trainable_weights)
           self.optimizer.apply_gradients(zip(grd,
           self.model.trainable_weights))
           self.acc_func.update_state(y, pred)
           return loss
       @tf.function
       def model_predict(self,x)
         return self.model.predict(x)

    3.おわりに

    これらのを利用することでTF1.x系でも、できた細かなカスタムや調整もできるようになったと思います
    最後にTensorFlow v2.x系はまだちょっと未完成なところ(ver2.0では勾配計算に失敗したり、今でのカスタムレイヤーで複数出力を計算時にバグがあったり)があるので研究等で使う場合は十分に気を付けてくださいませ
  • 広告
  • GregTech 5/5Uのエネルギーシステムについて(公式GTNH wiki「Electricity」日本語訳)

    2019-05-16 20:272

    この記事はGTNHの公式wikiの「Electricity」項目の日本語翻訳です(URL:https://gtnh.miraheze.org/wiki/Electricity)
    一部見やすいように改変していますのでご了承下さい

    2019/05/16:初版、1と2だけの翻訳
    2019/06/18:3を翻訳開始

    ⚡INDEX

    1. はじめに
    2. 電圧と電流
    3. ケーブルと損失
    4. 変圧器
    5. 機械の爆発とケーブルの焼失
    6. エネルギー変換
    7. エネルギー保存の管理
    8. 新規プレイヤー向けのガイド

    1.はじめに
    ⚠このページ及びGTNHの公式wikiの「Electricity」項目は未完成であり、情報を募集中です

    このページ及びGTNHの公式wikiの「Electricity」項目は、IC2Exのエネルギーシステムに不満をもったGregoriusT氏がGregTechにver5(Minecraft ver1.7.2向け)から実装したエネルギーシステムについての解説です
    よって全てのGregTech5のバージョンのエネルギーシステムの解説になります(非公式版も含む)
    IC2のエネルギーネットと互換性をなくした理由はケーブルの減衰が機能しないことと、そのネットワークにパケットが存在しない、そしてそれを整数型から倍精度浮動小数点型に変更したからです(より大きなエネルギーストレージは恐ろしいです)。言うまでもなく、タイルエンティティを常に登録及び解除しないでエネルギーの流れを制御することは非常に難しいことです
                                  ー GregoriusT


    2.電圧と電流
    GregTechは電圧(Voltage 単位:V)とアンペア(Ampere 単位:A)という用語を用いて新しい電力システムを実装しています。「1アンペア」はIC2のEUの1パケットと同じで、「電圧」はそのパケットのサイズと同じです。EU/tは受け取ったEUの合計値です。
    例えば、1つのマシンがある時に32Vのポケットを受け取り、そのあと24Vのポケットを受け取ったとしたら、受け取った合計のEU/tは32+24=56EU/tとなります。
    IC2のエネルギーシステムとは異なり、エネルギーによって作用する全てのGregTechのブロックは、それらが作用できる電圧とアンペアの両方に制限があります。
    機械によって入出力できるアンペアが異なります
    • GregTechの変圧器(Transformer)は昇圧時は、入力4A、出力1Aになり、降圧時は、入力1A、出力4Aになる
    • バッテリーバッファ(Battery Buffer)は1つのバッテリーにあたり入力2A、出力1Aとなる
    • バッテリー充電器(Battery Charger)は1つのバッテリーにあたり入力8A、出力2Aとなる
    • チェストバッファ(Chest Buffer)とスーパーバッファ(Super Buffer)は入力2Aとなる
    • エネルギーハッチ(Energy Hatch)は入力2Aとなる
    • 物質製造機(Mass Fabricators)は入力10Aとなる
    • マイクロウェーブエナジートランスミッター(Microwave Energy Transmitter)は入力3Aとなる
    • モンスターリペレンター(Monster Repellator)、ポンプ(Pump)、テレポーター(Teleporter)は入力2Aとなる
    • 他のすべてのEU受け取る機械はレシピに応じて少なくとも1Aを受け取ります:入力アンペア数はレシピで表示されるEU使用量の2倍を機械の入力電圧で割ったものに1を足したものです
      ・LVの遠心分離機が5EUのレシピを処理している場合、入力1Aとなる
      ・LVの化学炉が30EUのレシピを処理している場合、入力2Aとなる
      ・LVのアーク炉が96EUのレシピを処理している場合、入力は7Aとなる
    • 発電機は1Aを出力します
    機械名入力出力
     変圧器(Transformer 昇圧) 4A 1A
     変圧機(Transformer ※降圧) 1A 4A
     バッテリーバッファ(Battery Buffer) 2A(バッテリー1個あたり) 1A(バッテリー1個あたり)
     バッテリー充電器(Battery Charger) 8A(バッテリー1個あたり)
     2A(バッテリー1個あたり)
     チェストバッファ(Chest Buffer) 2A NONE
     スーパーバッファ(Super Buffer) 2A NONE
     エネルギーハッチ(Energy Hatch) 2A NONE
     物質製造機(Mass Fabricators)
     10A NONE
     マイクロウェーブエナジートランスミッター
     (Microwave Energy Transmitter)
     3A NONE
     モンスターリペレンター(Monster Repellator)
     2A NONE
     ポンプ(Pump)
     2A NONE
     テレポーター(Teleporter)
     2A NONE
     その他の全ての機械 ((レシピEU*2)/機械の電圧)+1A NONE
     発電機(全て) NONE 1A

    機械にエネルギーを入力する際に以下のことに注意することがある:
    • 機械の電圧より高い電圧を入力すると爆発します。機械は必要になるまでエネルギーを受け取らないので、実際に動作するまで爆発することはありません!
    • 機械に過剰入力されたアンペアは機械の電圧の限界を下回っている限り効果がありません。機械は電圧を必要としない限り電流を流さずアンペアの端数も流しません、これによって電力に関して機械は機械自身で調整します
    機械とレシピにはそれぞれ電圧のティアがあります。マルチブロックの機械はそのエネルギーハッチによって決定します。 機械のティアとレシピのティアは相互に作用するため、注意を払う必要があります
    • レシピのティアがマシンのティアより高かった場合、レシピは実行されません
    • レシピのティアがマシンのティアと同じだった場合、レシピは正常に動作します
    • レシピのティアがマシンのティアより低かった場合、レシピはオーバークロックされます。 オーバークロックされる場合は普通レシピの速度の2倍の速度で、2倍のエネルギーで実行されます。したがって、1ティックあたり普通レシピの4倍のエネルギーで実行されます
    レシピのオーバークロックは機械の電圧とレシピが必要とする電圧との差の電圧ティアの1づつに実行されます。 もし、30EU/t(LVティア)で20秒で作業が完了するレシピをHVのマシンで実行される場合、2つのティアの差によって480EU/tで5秒で作業が完了します
    GregTech:New Horizonsはバージョン2.0.2.5の時点で9つの電圧のティアが存在しています。そのうちの3つの電圧のティアは部分的な実装(*)で4つの到達不可能な電圧のティア(**)です
    ノート:ULVティアはティア0として扱います
    短縮形電圧名許容最高電圧
     ULV Ultra Low Voltage
     8
     LV Low Voltage
     32
     MV Medium Voltage 128
     HV High Voltage 512
     EV Extreme Voltage
     2048
     IV Insane Voltage
     8192
     LuV Ludicrous Voltage 32768
     ZPM ZPM Voltage 131072
     UV Ultimate Voltage 524288
     UHV(*) Highly Ultimate Voltage 2097152
     UEV(*) Extremely Ultimate Voltage 8388608
     UIV(*) Insanely Ultimate Voltage
     33554432
     UMV(**) Mega Ultimate Voltage
     134217728
     UXV(**) Extended Mega Ultimate Voltage
     536870912
     OpV(**) Overpowered Voltage
     1073741824
     MAX(**) Maximum Voltage
     2147483647

    3.電圧と電流
    GregTechには現在独自の電気システムがあるため、GregTechのマシンへの電源供給にはGregTechケーブルを使用する必要があります
    GregTechでIC2のEUを受け入れることができる唯一のマシンが[[GregTech5Uの変圧機(Transformer)|変圧器(Transformer)]](IC2の変圧器(Transformer)と混同しないでください)

    すべてのGregTechのケーブルには許容電圧と許容電流と損失があります:
    ・許容電圧より高いパケットを受け取るとケーブルは焼失してしまうでしょう
    ・許容電流より多くのアンペアを通電させたケーブルは焼失してしまうでしょう
     パケットが反発する可能性があることに注意してください。論理パスがEUをそこに流すべきではないとしても、そのケーブルに余分なパケットが流れない事を絶対に考えてはならない(必ずしも最適な電流がそのケーブルを流れるとは限らないという事。例えば分岐させたからと1Aのケーブルでも大丈夫ではなく、出力アンペアと同じかそれ以上に耐えうるケーブルを使わないと反発し合った時に焼失するおそれがある)
  • Spigot/Bukkit/Sponge/Bungeeプラグイン「Plan|Player Analytics」解説(2019/09/27更新:Plan v5.0のテストビルドに日本語ファイル実装)

    2019-03-01 02:14

    02/28の生放送でいろいろとやっていた設定していたプラグインです
    このプラグインは多種多様の機能を兼ね備えて導入必須というレベルで素晴らしいプラグインですが、日本語の解説が0と言う悲しい事実なので解説していきます。コンフィグの詳しい説明とかはよくわかっていないのでそこらへんは割愛します
    対応バージョンは1.7~1.13
    配布先:https://www.spigotmc.org/resources/plan-player-analytics.32536/
    開発GitHub:https://github.com/Rsl1122/Plan-PlayerAnalytics
    Wiki:https://github.com/Rsl1122/Plan-PlayerAnalytics/wiki

    2019/04/24:Plan ver4.8.2 DEV2にて日本語化ファイルが実装され使えるようになりました
    また日本語翻訳に関するGitHubフォークとDiscordを立ち上げました
    日本語化に関するGitHun:https://github.com/yukieiji/Plan-JapaneseTranslationProject
    日本語化に関するDiscord:https://discord.gg/cF5KFgP
    2019/05/10:Plan ver4.8.2が正式リリースされ日本語ファイルが正式に実装されました、下の方にやり方を書いておきます
    2019/09/27:Plan ver5.0のテストビルドも日本語化が完了しました、インターフェースが一新されています。DL先→ :https://plan.djrapitops.com/job/Player%20Analytics/job/interface-redesign/


    1.Plan|Player Analyticsって?ああ!!
    聞いたことすらないプラグインだと思います。Planは導入されているサーバーの各種情報をブラウザ上で確認できるプラグインです。
    プレイヤーの各種情報やプレイヤーがどれくらい活動してるのか、KDR、プレイヤーのPingなどいろいろと確認できます
    更に下の画像の様にサーバーの負荷状況、メモリ使用率、TPS、CPU使用率なんかも確認できます。ここに書いてある機能はごく一部で、配布先の画像に機能一覧を載せた画像があります
    データベースを使用することもできるため、ある程度高速に動作します



    2.Spigot/Bukkit/SpongePlan|Player Analyticsの導入
    何時ものようにプラグインフォルダにダウンロードしたjarファイルを入れて再起動するだけで導入自体は完了です。
    デフォルトのPlanの使用ポートは8804番で、ブラウザに「http://localhost:ポート番号」と入力して表示されていれば成功です。ポート番号を開放すれば外部からも接続可能です
    データベース関連を使用する場合、コンフィグより使用するデータベースなどを入力します。
    プレイヤーの情報が表示されない場合は「/plan analyze」などをつかって分析をかけます
    2019/03/07更新:更新でデーターベース関連が変更されてデフォルトのままだと動かなくなりました。なのでSQLiteかMySQL(MarinDB)を導入してプラグインのコンフィグを設定するか、コンフィグのデーターベースをH2に設定してください

    3.Bungee/WaterFallへPlan|Player Analyticsの導入
    Bungee/WaterFallへの導入はMySQLの導入や入れた後の設定が必要のためちょっとめんどくさいです。(MySQL以外もできるけどやり方がWikiに書いてないので割愛)
    3-1:まずMySQLを導入予定のBungeeが実行されるマシンにインストールします
    3-2:インストール後MySQLをPC起動時に自動実行するようにします
    3-3:MySQLのルートパスワードを設定しMySQLのコマンドラインを起動しログインします(Linuxとかだと管理者権限で「mysql -u root -p」を実行するとコマンドラインが起動します)
    3-4:Planが使用するデータベースを「CREATE DATABASE (データベース名);」で作成
    3-5:Planが使用するデータベースを操作するユーザーを「CREATE USER '(ユーザー名)'@'localhost' IDENTIFIED By '(ログインパスワード)';」で作成、ユーザー名とパスワードは覚えておいてください
    3-6:上で作ったユーザーにPlanが使用するデータベースの操作権を「GRANT ALL PRIVILEGES ON (データベース名).* TO '(ユーザー名)';」付与
    3-6ex1:Bungee/WaterFallにつなげるサーバーがBungee/WaterFallと同じマシン上にない場合、「GRANT ALL PRIVILEGES ON (上で作ったデータベース名).* TO '(上で作ったユーザー名)'@'繋げるサーバーのIPアドレス' IDENTIFIED BY '(上で作ったユーザーのログインパスワード)' WITH GRANT OPTION;」を使って外部サーバーからMySQLを操作できるようにする
    3-6ex2:そしてBungee/WaterFallのサーバーの3306番ポートを開放、別の外部サーバーがある場合3-6ex1を繰り返す
    3-7:PlanをBungee/WaterFallのプラグインフォルダに導入して起動(切らなくておk)
    3-8:起動したらPlanのコンフィグを開く
    3-9:Server:のIP:の項目に自身のBungee/WaterFallに接続できるIPもしくはアドレスを入れる
    3-10:Database:のMySQL:内にあるUser:に上のPlanが使用するデータベースを操作するユーザー名、Password:に上のPlanが使用するデータベースを操作するユーザー名のパスワード、Database:に上のPlanが使用するデータベース名を入れる
    3-11:上記設定が終了後保存して「/planbungee reload」と入力してコンフィグをリロード、ブラウザに「http://localhost:ポート番号」と入力してウェブページが表示されることを確認する
    3-12:Bungee/WaterFallに接続されている全てのSpigot/Bukkit/SpongeにPlanを上の2にある通りに導入し起動
    3-13:Spigot/Bukkit/SpongeのPlanのコンフィグを開いてサーバー名を適当に変えておく
    3-14-1:もしBungee/WaterFallを実行している同じマシンのサーバーならBungeeのPlanで使用してないポートの番号をWebserver:のPort:に入れる(WikiだとFreeって書いてあるから空欄にするんかなって思うけど次の作業でうまくいかないのでなんか入れとく)
    3-14-2.1:もしBungee/WaterFallを実行しているマシンと違うマシンで実行されているサーバーならPlanのコンフィグが使用しているポートの番号を開放
    3-14-2.2:Planのコンフィグを開きWebserverの欄の「Alternative_IP: true」に変更、その下の項目を「Address: "実行しているサーバーマシンのIP:%port%"」とする(%port%は自動的に決まるらしいのでこれで良いとのこと)
    3-14:Bungee/WaterFallのほうで「/planbungee setup」と入力してセットアップモードを起動
    3-15:Spigot/Bukkit/Spongeの方で「plan m setup (BungeeのPlanのアドレス):(BungeeのPlanが使用するポート番号)」と入力、BungeeのPlanのIPアドレスはBungeeが同じマシンにあるなら「localhost」、Bungeeが同じローカルネットワーク内ならローカルのIP、それ以外ならサーバーに接続するIPorアドレス、IPアドレスやローカルホストの場合は「http://」を最初につけないとだめみたいです
    3-16:Bungeeのログに「Config file for server '(上で変更したサーバー名)' updated in /Plan/serverConfiguration」って出ていれば完了、Bungeeに接続されている別のサーバーがあれば、3-14より再度設定
    3-17:「/planbungee reload」でPlanをリロード
    3-18:ブラウザに「http://localhost:ポート番号/server」で追加されているサーバーがあれば導入完了
    3-19:Spigot/Bukkit/SpongeのPlanフォルダ内にあるデータベースファイルを消しておく(Bungeeのデータベースに統合されるため)

    3.日本語化やり方
    1.Plan ver4.8.2以降を導入しSpigot/Bukkit/Sponge/Bungee/WaterFallを起動しコンフィグファイルを作成
    2.コンフィグファイルを開き「Plugin」の「Logging」の中の「Locale: default」を「Locale: JA」に変更3.「Formatting:」の「Time_amount:」を以下のように変更

    Year: '1年, '
    Years: '%years% 年, '
    Month: '1ヶ月, '
    Months: '%months%ヶ月, '
    Day: '1日 '
    Days: '%days%日 '
    Hours: '%hours%時間 '
    Minutes: '%minutes%分 '
    Seconds: '%seconds%秒'
    Zero: '0秒'

    3.完了後保存し「/plan reload」/「/planbungee reload」としてPlanを再起動、反映されない場合はSpigot/Bukkit/Sponge/Bungee/WaterFallを再起動する
    3ex.コンフィグの「Network」/「Server」の「Name:」の欄は必ずアルファベットのA~Z、a~zまたは1~9のみを用いた名前に設定し、ウェブページを開いたときにコンフィグで設定した名前になっていることを確認する(日本語へ翻訳する文字列が使われていた場合、サーバー名やネットワーク名まで翻訳され正しく動作しなくなります)


    4.余談

    いくつか連携しているプラグインもあるみたいです ⇒ https://github.com/Rsl1122/Plan-PlayerAnalytics/issues/583