sprocket icon indicating copy to clipboard operation
sprocket copied to clipboard

このリポジトリの目的

Open r9y9 opened this issue 7 years ago • 11 comments

このリポジトリのコードが提供する目的を、明確にしませんか?という話です。僕は主に、研究者がコードを公開する上で、以下の2つの意義があると思っています。

再現性の保証

Code to reproduce XX paperといったように、論文の実験結果を再現するためのコード、という位置付けです。例えば深層学習の分野では、xxxNetなどという名前で、日々アップロードされています。ベースラインとして他の研究の役に立つので、とても価値があると思います。

汎用的なツールの提供

他の研究の役に立つ汎用的なツールとして、 の位置づけです。音声分野では、SPTK, HTSなどが相当します。例えばSPTKは、小さな音声処理のプログラムを(シェルのパイプによって)組み合わせることによって、音声処理における多くの問題に汎用的に使用できるように設計されています。他の研究の役に立つので、これもとても価値があると思います。

現状の整理

で、何が言いたいかと言いますと、今後コードを改善していく上で、まず、どちらにどの程度着目するのか、という点について明確にしませんか、ということです(その他の目的があれば、補足お願いします)。それによって、今度どうすべきかが変わってくると思います。

もし前者全振りならば、考えることはそんなに多くないと思うのですが、もし後者も目的に設定する場合(できればそうしたいとは思いますが)、メンテも必要になってきますし、やることも多くなってくると思うので、大きな実装を始める前に、設計について議論すべきだと思います。考えるべき点について、いくつか挙げると、

  • そもそもどういう使用方法を想定するのか?ライブラリとしての使用(import hoge のような使い方)?それともコマンドラインツールとしての使用? or 両方?
  • 現状GMMベースの方法以外では使えないような設計になっているので(GMMTrainerありきですよね)、変換モデルに対する抽象化が必要ではないか
  • 特徴量にメルケプストラム以外使えない設計になっているので(FeatureExtractorにベタ書きされています)、特徴量に対する抽象化が必要ではないか
  • 分析合成系にはWORLDしか使えないようになっているので(昔Analyzer/Syntehsizerという共通インタフェースを作ったけど、使われていないので…)、STRAIGHTなりSPTK(のF0抽出手法各種)なり、その他分析合成系が使えるよう、抽象化が必要ではないか
  • 非パラレルデータからの声質変換も視野にいれる or not?
  • 一対多、多対一、多対多声質変換、etc

などです。あまり汎用性を意識しすぎても、何も手を付けられれないので、どこかにポイントを絞るべきかなと思ったりしますが、まずは明確にしないと、特に複数人での開発が難しいように思います。

So what

上記を踏まえて、僕から言いたいことをまとめると、

  1. Code to reproduceという点に割りきって、汎用性の優先度を下げて最速でリリース可能なようにissueを片付ける?run_sprocket.sh を実行すると、xxの実験結果を再現することができます、という形にする(それがゴール)。現実的です
  2. or ライブラリとしての設計を考えた上ですすめる?現状は、自分も関わっていて申し訳ないですが、上記考えるべき点でも挙げた通り、汎用的なツールであるとはいいにくいと思います。この方針で行く場合は、まず設計(≒何をゴールとするか)を考えないといけないと思います。

ご意見よろしくお願いします。

r9y9 avatar Jun 28 '17 05:06 r9y9

ご意見ありがとうございます.このレポジトリの今後の方向性を考えるのに非常に重要な意見かとおもいます.

そもそも何故sprocketの開発を始めたかというと,Voice Conversion Challenge 2018(予定)のベースラインシステム(VCC2016のチャンピオンシステムを少し改良した手法 [Kobayashi et al., SLT 2016])として,再現性のあるコードを提供する事が目的です.第一に,私が重要だと思っていた点として以下があります.

  1. VCC2018のために.SLTで提案した手法を再現出来る事
  2. 動的時間伸縮で結合特徴量を作成出来る事
  3. 現状の高品質な分析合成系を使える事

とりわけ,2に関しては,最近の論文には,記載されていないノウハウ(e.g., 変換特徴量と目標特徴量を用いた再アライメント,パワーが小さい部分の削除してのDTW)などを入れる事で,声質変換の研究の裾やを広げる上で重要かと思いました.また,最近の研究では,Model部分に関する研究が多く行われており,sprocketで得られた結合特徴量を用いて,Modelの研究に取り組めるのではないかと考えております.

3.に関しては,sprocketでは,上を満たすものであれば,どの分析合成系を用いても良いかと考えています.とりわけ,sprocket内で,様々な分析合成系を切り替えて用いる予定も現状はありません.一方で,例えば,world mcepで学習を実行し,中間ファイルとして得られたtwfを用いて,任意の音響特徴量の結合特徴量を作成するインターフェースは,用意出来ればと考えております.


  • そもそもどういう使用方法を想定するのか?ライブラリとしての使用(import hoge のような使い方)?それともコマンドラインツールとしての使用? or 両方?

import hogeの様な使い方を想定していました.イメージとしては,scripts/src/train_GMM.pyのGMMTrainerの部分をDNNTrainerなりNMFTrainerみたいなのをユーザが実装して,結合特徴量jntを使って学習すれば良い様にしたいです.

  • 現状GMMベースの方法以外では使えないような設計になっているので(GMMTrainerありきですよね)、変換モデルに対する抽象化が必要ではないか

前述した事と重なりますが,結合特徴量を出す所までがsprocketの範囲でその先は,ユーザが実現したいものを作ればよいかと思います(他のモデル化手法に関しても実装して行きたいとは考えています.).現状GMMが動けば良いと考えてます.ただし,GMMTrainerは,TrainerとConverterに分離すべきだったかと思ってます.

  • 特徴量にメルケプストラム以外使えない設計になっているので(FeatureExtractorにベタ書きされています)、特徴量に対する抽象化が必要ではないか

  • 分析合成系にはWORLDしか使えないようになっているので(昔Analyzer/Syntehsizerという共通インタフェースを作ったけど、使われていないので…)、STRAIGHTなりSPTK(のF0抽出手法各種)なり、その他分析合成系が使えるよう、抽象化が必要ではないか

声質変換の研究では,特徴量を色々変えて手法の有効性を確認したりする事はあまり多くなく,特徴量を固定して,変換モデルを切り替えて有効性を確認する事が多いです.つまり,分析合成系は出来る限りsprocketから切り離して実装したいと考えています.ただし,前述のとおり,他の特徴量を扱えるインターフェースを用意する予定です.

  • 非パラレルデータからの声質変換も視野にいれる or not?

出来たとしても当分先になりそうです.

  • 一対多、多対一、多対多声質変換、etc

結合特徴量があれば,新たにシェルを作成する事で,簡単なver.であればこちらの方が先にできそうではあります.やるかは,未定です.(sprocket_レシピ)みたいなレポジトリを用意して動くようにしても良いかと思います.


  1. Code to reproduceという点に割りきって、汎用性の優先度を下げて最速でリリース可能なようにissueを片付ける?run_sprocket.sh を実行すると、xxの実験結果を再現することができます、という形にする(それがゴール)。現実的です
  2. or ライブラリとしての設計を考えた上ですすめる?現状は、自分も関わっていて申し訳ないですが、上記考えるべき点でも挙げた通り、汎用的なツールであるとはいいにくいと思います。この方針で行く場合は、まず設計(≒何をゴールとするか)を考えないといけないと思います。

私が未熟なので,本当は2を作りたかったのに,気づけば1になっていたというのが現状です.申し訳ない.1の再現性部分に関しては,概ね出来てるといえるので,2の方にシフトしていく上で力を貸して欲しいです.ちょっとどういう機能があれば,良いかの資料を作成します.

k2kobayashi avatar Jun 29 '17 03:06 k2kobayashi

返信おそくなりました。すいません

そもそも何故sprocketの開発を始めたかというと,Voice Conversion Challenge 2018(予定)のベースラインシステム(VCC2016のチャンピオンシステムを少し改良した手法 [Kobayashi et al., SLT 2016])として,再現性のあるコードを提供する事が目的です.

なるほどです。素晴らしいと思います。

  1. VCC2018のために.SLTで提案した手法を再現出来る事
  2. 動的時間伸縮で結合特徴量を作成出来る事
  3. 現状の高品質な分析合成系を使える事

ライブラリとして考えると、この内重要なのは2でしょうか。

今後の方向性として、一つの案としては、このリポジトリでは2にフォーカスする、というのがいいと思いました。1は、そのライブラリを活用した一例として、再現性を担保する目的で、別リポジトリに切り分ける、という方法です。現状、両者を一緒にしてしまっていることが良くないように思います。特に、リポジトリトップ/sprocketリポジトリトップ/scripts/src の2つを整理する必要があると思います。この方針は、汎用性を担保しやすい一方で、声質変換におけるごく一部のパートのみを提供することになり、ユーザが自分で書かないといけないコードはそれなりに多くなります(特徴抽出、モデル学習、変換、合成、etc)。研究者向けなら、何の問題もないとは思いますが、一般ユーザも対象にする場合は、優しくないかもしれません。3は、WORLDを使うことで自然と達成されると思います。

別の方向性として、声質変換のフレームワークを目指す方針があると思います。例えば、研究者が重要な部分の研究(モデル?)に専念できるように、それ以外の部分の共通化を目指す、とかです。数年前からずっと考えていますが、いまだに良い案が浮かびません。過去の僕の失敗作として、 https://github.com/r9y9/VoiceConversion.jl があります。

import hogeの様な使い方を想定していました.イメージとしては,scripts/src/train_GMM.pyのGMMTrainerの部分をDNNTrainerなりNMFTrainerみたいなのをユーザが実装して,結合特徴量jntを使って学習すれば良い様にしたいです.

この部分をもっと具体的にする必要があると思います。GMMTrainerに関しては、モデルと変換を分けるのはもちろん、ファイル入出力も切り離すべきだと思いますが、それはさておきとして、現状では、

  • XXXTrainerを作るにしても、何のメソッドを実装すればいいのか非自明
  • GMMTrainerはJointFeatureExtractorでも使われているので、例えばGMMではなくDNNを使いたい場合は、JointFeatureExtractorを修正する必要がある

といったように、sprocketのコードをそのままでは使い回せません。GMMベースの以外の方法では使えない、GMMTrainerありきと書いたのは、補足するとこういうことです。JointFeatureExtractorがsprocketの重要な部分であり、ユーザに使ってもらうものであるとすれば、任意のTrainerで使用できるように、Trainer部分を抽象化する必要があると思います。じゃあどうすればいいかと考えると(そもそもTrainerとは何なのか、本当に必要なのかといった議論は一旦さておき)、僕ならBaseTrainerのようなクラスを作り、ユーザはそのクラスを継承していくつかメソッドを実装すればOK、といった設計にします。JointFeatureExtractorは、GMMTrainerではなくBaseTrainerなら何でも動くように実装します。

class BaseTrainer(object):
    def train(X):
        raise NotImplemented("必ず実装してください")
class DNNTrainer(BaseTrainer):
    def train(X):
        ${my_train_code}

同様のことが特徴量に関しても言えます。

声質変換の研究では,特徴量を色々変えて手法の有効性を確認したりする事はあまり多くなく,特徴量を固定して,変換モデルを切り替えて有効性を確認する事が多いです.つまり,分析合成系は出来る限りsprocketから切り離して実装したいと考えています.ただし,前述のとおり,他の特徴量を扱えるインターフェースを用意する予定です.

分析合成系を切り離すことは同意ですが、特徴量を変えて手法を検討することは、多くはないかもしれませんがそれなりにあると思います。少なくとも僕はそうしたいです。メルケプストラムではなく対数スペクトルを使うこともあれば、メルケプストラムの次元数を変えて実験することあると思います。

私が未熟なので,本当は2を作りたかったのに,気づけば1になっていたというのが現状です.申し訳ない.1の再現性部分に関しては,概ね出来てるといえるので,2の方にシフトしていく上で力を貸して欲しいです.ちょっとどういう機能があれば,良いかの資料を作成します.

ありがとうございます。僕も未熟ですが、助けになれるようがんばります。

いろいろ書きましたが、実験を再現するためのコードと、汎用的な部分とでリポジトリを分けるのは、悪くないと思いますが、どうでしょうか。分けることで、何が汎用的なのか、自然と整理されていくような気もします。

r9y9 avatar Jun 30 '17 06:06 r9y9

今後の方向性として、一つの案としては、このリポジトリでは2にフォーカスする、というのがいいと思いました。1は、そのライブラリを活用した一例として、再現性を担保する目的で、別リポジトリに切り分ける、という方法です。現状、両者を一緒にしてしまっていることが良くないように思います。特に、リポジトリトップ/sprocket と リポジトリトップ/scripts/src の2つを整理する必要があると思います。この方針は、汎用性を担保しやすい一方で、声質変換におけるごく一部のパートのみを提供することになり、ユーザが自分で書かないといけないコードはそれなりに多くなります(特徴抽出、モデル学習、変換、合成、etc)。研究者向けなら、何の問題もないとは思いますが、一般ユーザも対象にする場合は、優しくないかもしれません。3は、WORLDを使うことで自然と達成されると思います。

今,リポジトリトップ/scripts/srcの中にあるスクリプトは,特徴抽出やモデル学習などの,それぞれ独立した処理となっています.想定してるユースケースとしては,このスクリプトの一部(例えば,extract_feature.py)をユーザが自前で書き換えて,新たな特徴量に新たなラベル'mcep_refined'をつけてh5に入れる事で,その後の処理が動けばよいと考えています.

この部分をもっと具体的にする必要があると思います。GMMTrainerに関しては、モデルと変換を分けるのはもちろん、ファイル入出力も切り離すべきだと思いますが、

おっしゃる通りです.

それはさておきとして、現状では、XXXTrainerを作るにしても、何のメソッドを実装すればいいのか非自明

XXXTrainerの名前が悪かったかもしれません.jntを使ってユーザがDNN.train(jnt)やDNN.convert(x)などの関数を実装して,ユーザの領域で学習と変換を実現すれば良いと考えています.言い換えると,sprocketは,ユーザにjntやxを提供する機能だけを持つという事です.

GMMTrainerはJointFeatureExtractorでも使われているので、例えばGMMではなくDNNを使いたい場合は、JointFeatureExtractorを修正する必要がある

ここで,仮にDNNを変換モデルとした場合でも,JointFeatureExtractorは,GMMによる変換特徴量を使ってtwfを抽出しjntを得る事を考えています.その後,ユーザがDNNなどで学習変換して,その変換特徴量をJointFeatureExtractorやそれに準ずる未作成の関数に渡す(e.g., jnt.twf_refine(cv_x, tar))と新しいtwfが作成され,それに基いてjntを再抽出できるインターフェースを用意すれば良いと考えています.もしくは,JointFeatureExtractorクラスを削除して,ユーザ側(つまり, scripts/src/estimate_twf.py)に全ての処理をsprocketの関数を利用して実装すれば,DNNによる学習・変換は容易かと思います.

僕ならBaseTrainerのようなクラスを作り、ユーザはそのクラスを継承していくつかメソッドを実装すればOK、といった設計にします。JointFeatureExtractorは、GMMTrainerではなくBaseTrainerなら何でも動くように実装します。

JointFeatureExtractorに異なるTrainerClassやConverterClassを与えるのは,色々と変換システムを切り替えられるので良いアイディアと感じましたが,ユーザに,sprocket内の流儀に従うコードの実装を出来る限りさせたくないと考えています(実際,その様に実装は出来てないと思いますが).

k2kobayashi avatar Jul 02 '17 07:07 k2kobayashi

jntを使ってユーザがDNN.train(jnt)やDNN.convert(x)などの関数を実装して,ユーザの領域で学習と変換を実現すれば良いと考えています.言い換えると,sprocketは,ユーザにjntやxを提供する機能だけを持つという事です.

なるほど理解しました。ただ、sprocketの機能を結合特徴量の提供にフォーカスするのは良いと思いますが、その場合、scipts/src内のスクリプトを書き換えて使用する、というユースケースは、ふさわしくないのではないでしょうか。スクリプトには結合特徴量の抽出以外の処理が多くあり、それを使わせるということは、(結合特徴量の抽出以外の機能を提供しているという点で)そもそもの目的に反するように思います。また、ユーザにsprocket内の流儀に出来る限り従わせたくないという意味でも、スクリプトの使用を想定する場合、決まったディレクトリ構造、ファイルフォーマット等をユーザに強制することになってしまいます。

もちろんユースケースとしてはscripts内のような方法も当然あるのですが、ライブラリの設計を考える上では、もう少し詳細に、どのモジュールのどのクラス/関数を、どのように(組み合わせて、拡張して)使用するのか、というユースケースを考えた方が良いかなと思います。ユーザーにとってのインタフェースは、import hoge する部分であり、それを通して呼び出す関数、クラスであるので。 jnt.twf_refine(cv_x, tar) みたいなインタフェースがあると、便利だと思います

もし import hoge ではなく、スクリプトをちょっといじって使用してもらう想定であれば、スクリプトをいじらなくて済むようにオプションを充実させた、コマンドラインツールとしての提供もありかもしれません

r9y9 avatar Jul 02 '17 09:07 r9y9

なるほど理解しました。ただ、sprocketの機能を結合特徴量の提供にフォーカスするのは良いと思いますが、その場合、scipts/src内のスクリプトを書き換えて使用する、というユースケースは、ふさわしくないのではないでしょうか。スクリプトには結合特徴量の抽出以外の処理が多くあり、それを使わせるということは、(結合特徴量の抽出以外の機能を提供しているという点で)そもそもの目的に反するように思います。また、ユーザにsprocket内の流儀に出来る限り従わせたくないという意味でも、スクリプトの使用を想定する場合、決まったディレクトリ構造、ファイルフォーマット等をユーザに強制することになってしまいます。

その通りですね.そもそもの弊害は,一連のVCを動作させるために各要素(特徴抽出やGMMの学習変換)を密結合して実装していった事にあると思います.加えて,confの様な処理に必要なパラメータを謎のクラスにまとめてしまった事もかなり良くなかったと思います.前言のjntの抽出に焦点を当てたものとは異なりますが,sprocketに関しては,VCの基盤となる関数やクラスを提供する枠組みとして捉え,他のレポジトリでsprocketを利用した差分VCの実装を実施するべきな気がしてきました.とても良くいうとsklearnのVC専門の様な感じが理想的かもしれません. たとえば,import sprocket as spr と宣言して,gmm = spr.model.GMM(混合数など)を読み込むと,gmm.train(jnt)やgmm.convert(x)が出来るような枠組みをsprocketでは提供出来ればと思います.他にも,feat = spr.feature(fs = 16000)みたいなf0= feat.f0analysis(x, mode='sptk')とユーザに簡単に分析処理が実現できる様なクラスを提供出来ればと思います.(特徴量に関してはラッパーにがほとんどになると思いますが).JointFeatureExtractorをsprocket内に用意するかに関してはかなり微妙なラインですが,デフォルトをGMMとして,sprocket内にspr.model.DNNが実装された場合に変換システムを変更出来る様な枠組みのクラスと捉えると,ユーザー側には不便に感じるかもしれませんが,そういったクラスを用意出来るかもしれません.また,dtwなどは,現在は,xとyのフレームの重複を許す仕様になってますが,どちらか片方の重複を許さない場合(つまりjoint featureの長さがlen(x) or len(y)になる)様な関数も用意しておけば,深く使う人にとっても有意義なframeworkになる気がします. あとは,他のレポジトリにsprocketを使った1-to-1VCやEVCなどを実現出来るコードを用意すると良いかと思います.

k2kobayashi avatar Jul 02 '17 09:07 k2kobayashi

とりあえず,script内は結合テストのコードの様にみなして,本番公開時には別レポジトリにわけると良いと思います.

k2kobayashi avatar Jul 02 '17 10:07 k2kobayashi

再現性の保証ではなく、ライブラリを有用なものにするためにどうすればいいか、という点について、話を進めます。まずは今後の方針が不明確に感じるので、明確にしていきたいと思います。まず、声質変換の研究者向けとするのか、(声質変換、音声処理に詳しくない)一般ユーザ向けとするのか、をはっきりさせるのが良いと思います。

#30 のコメントと https://github.com/TakedaLab/sprocket/issues/23#issuecomment-312481778 から想像するに、high-levelなインタフェース(≒blackboxとして動く、声質変換、音声処理の専門外ユーザ向け)を想定しているのだと思いますが(※もし間違っていたら指摘してください)、個人的には、これは一般ユーザにも研究者にも役に立たないものになりかねない、と危惧しています。

high-levelであるということは、low-levelな実装は隠蔽されているので、low-levelな部分を変えたい場合には、自分で書かなければなりません。例えばscikit-learnは、機械学習の一般ユーザ寄りのライブラリです。GMMを使って何かを学習したい人にはうってつけですが、GMMの学習アルゴリズムについて研究している研究者には、あまり役に立たず結局自分で書かなければならない、といったようにです(GMMは例です)。

声質変換は、多くの要素技術からなる応用寄りの技術で、要素技術として他の技術のベースに使用されることは少ないように思います。要素技術として活きる技術であれば、blackboxとして動くツールの価値は大きいと思いますが(WORLD, STRAIGHTなどがそうです)、そうでない場合、あまり価値が見いだせないのではないか、と思いました。つまり、high-levelなインタフェースでは、声質変換の専門外の人にとっても役に立たないのではないか、ということです。

たとえば,import sprocket as spr と宣言して,gmm = spr.model.GMM(混合数など)を読み込むと,gmm.train(jnt)やgmm.convert(x)が出来るような枠組みをsprocketでは提供出来ればと思います.

言い換えれば、このような使い方をする人がそもそもいないんじゃないか?ということです。研究者はtrain/convertの中身をいじりたいし、convertしたあとの結果を使って何か別の応用をするユーザなんてほぼいないんじゃないか、と

で、今後の方向性について僕の意見をまとめると、high-levelなライブラリは、low-levelなライブラリの上に必要であれば作ることができる、という意味でも、声質変換の研究者をターゲットとして、low-levelなライブラリにする方針がよいと思っていますが、いかがでしょうか。僕が気づいていないだけで、high-levelなインタフェースにはこんな価値がある、などあれば、教えてください。

r9y9 avatar Jul 09 '17 07:07 r9y9

このレポジトリで開発しはじめた頃から考えていたのは,pythonとVCに初めて触れたB4の学生くらいが研究の事始めとして使えるレポジトリになれば良いと思ってました.

声質変換は,"特徴抽出->アライメント->学習->変換->合成"とざっくりみると5つの要素からなっていて,これらの一連全てが動くコードをターゲットとする人たちに提供し,その一部を改変してもらう(主に,学習と変換)事で,研究をスタートさせれば良いかなと思っています.あと他には,聴覚系とかの音声分野に関連する人たちにサクッと使って貰えれば良いと考えています.

low-level,つまり,最大限機能を分離したインターフェースを提供する事は,上の4つをより細かく分離していく事に相当すると思うのですが,low-levelな機能をガリガリ自前で実装して追加していく能力はないので,ライブラリとして充実したものにはならない様な気がします.

それよりも,sprocket内でフワっと呼ぶと使えるインターフェースがあれば良いかなと思います. 音声波形->[特徴抽出機]->音響特徴量 が出て来るインターフェースがあって,パラメータを変えると特徴量の種類が変わるものや,

  • 特徴量群(特徴量ベクトルのリスト)->[アライメント]->jnt
  • jnt -> [GMMTrain] -> GMMparams
  • x, GMMparams -> [GMMconvert] -> y
  • F0, y_mcep, y_bap -> [Synthesizer] -> y_wav くらいで動くのが良いと思います.我々は,[]の機能をsprocketで提供すれば良いと考えています.

そして,上の5つの要素を繋いだexampleを用意し,データを用意すれば1スクリプトで動く様にして提供すれば良いのではと思います.※以前にも議論がありましたが,[アライメント]の分離具合は議論がまだ必要かも

k2kobayashi avatar Jul 12 '17 04:07 k2kobayashi

この前,電話MTをした上で決まった事から抜粋してメモしておきます. 本格的な部分ちすては,再現性全振りで公開が目的.その後適宜修正を入れて更新していく.

TODO:

  • Shifterの統合
  • ドキュメントの整備
  • exampleをマージしてから
  • pyworldの導入
  • 名前が入るのはおっけ.

k2kobayashi avatar Jul 19 '17 09:07 k2kobayashi

このレポジトリで開発しはじめた頃から考えていたのは,pythonとVCに初めて触れたB4の学生くらいが研究の事始めとして使えるレポジトリになれば良いと思ってました.

無意識に汎用的であることを重視していろいろ考えていましたが、ふと思いつきで意見すれば、B4などを対象とした教育目的なコードを提供する、という方針はありですかね?(あまり深く考えずに言っています

r9y9 avatar Aug 13 '17 16:08 r9y9

B4に対する教育目的なコードを提供する方針というのは,とても良くて,jupyter notebookなどで処理内容を追いつつ,sprocketのライブラリを理解出来る手段を作れればと考えています.

k2kobayashi avatar Sep 29 '17 08:09 k2kobayashi