2018年12月10日月曜日
縫製工場の類似度算出を深層学習のモデルでやってみた
シタテル株式会社CTOの和泉です。
Follow @shinobu_shiva
今回は研究開発的に、「縫製工場の類似度算出を深層学習のモデルでやってみた」について書きます。
まず初めに結果から。
['loss', 'acc']
[273999.25, 0.695652186870575]
69.6% 成功?なにが?って話ですよね。
結果的には全然精度出ませんでした。まぁ、のべ1週間ぐらいでやってみた感じですし、サーベイも全然できてないので結果出るわけないのですが。
せっかくやってみたのでブログに恥を晒しておきます。
その道の専門の方にはお目汚しかと思いますがご容赦ください。
やりたかったこと
弊社コンシェルジュが手動で類似分類(グループ化)した縫製工場 のデータを教師データとして学習し、同様の類似分類を行える学習済みモデルを作成する。類似分類ができれば、これまでのアイテム作成履歴から代替となる工場の提案ができる。かも。
希望的仮定
類似度の判定に用いられる特徴はある程度絞られているのではないかと想定し、学習によって効果の高い特徴が反映されるモデルが作成されれば、人の判断に近づけるのではないか。結果
教師データに対しては98.8%の正答率に達したが、テストデータについては69.6%にとどまった。残念ながら実用に足るような結果は得られなかった。
図1:教師データに対する推論結果のプロット(青が教師データ、点線先のオレンジが対応する推測結果)

図2:テストデータに対する推論結果のプロット(青が教師データ、点線先のオレンジが対応する推測結果)

図3:学習の経過
何をやったのか
データベースに保有する123の縫製工場を2次元平面上で類似しているものを近くに配置するように、弊社コンシェルジュに依頼して教師データを作成。(図1の青い点が各工場を表す)この際、「類似している=あるアイテムを相談するときに変わりに相談できる工場であるか」とした。作成された教師データが含む縫製工場データは114件。9件は情報不足により配置できなかった。作成した教師データは各工場ごとに位置ベクトルが与えられた状態になっている。この位置ベクトルを各工場がもつ特徴ベクトル(全313次元のうち、名前などヒューリスティックに取り除けるものを除いて、108次元)から求めるモデルを学習により獲得した。
DNNモデルや条件などのメモ
model = Sequential()
model.add(Dense(DIM, activation='relu', input_dim=DIM))
model.add(Dropout(0.25))
model.add(Dense(DIM*2, activation='relu'))
model.add(Dense(2))
model.compile(optimizer='adam',
loss='mean_squared_error',
metrics=['accuracy'])

_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense_1 (Dense) (None, 108) 11772
_________________________________________________________________
dropout_1 (Dropout) (None, 108) 0
_________________________________________________________________
dense_2 (Dense) (None, 216) 23544
_________________________________________________________________
dense_3 (Dense) (None, 2) 434
=================================================================
Total params: 35,750
Trainable params: 35,750
Non-trainable params: 0
_________________________________________________________________
history = model.fit(X_train, Y_train,
batch_size=50,
epochs=5000,
validation_split=0.1,
verbose=1)
考察
代替工場を考える際には、アイテムによって代替として扱うものが変わることが想定されるため、全体を2次元平面上に次元縮退してプロットすることが難しいであろうことは当初予定していたとおりであった。今回は、比較的手軽に作成できる教師データを用いて試してみたが、十分な結果は得られなかった。
統計によるリコメンドではデータの量が問題になるが、良質で量のあるデータが十分にないのが現状。シタテルの取扱量が増えることによりある程度のデータは蓄積されていくが、滝行との連携も含めて扱えるデータ量を増やしていくことが必要である。
工場データベースについて
シタテルは500を超える縫製工場やサプライヤーと連携して衣服づくりサービスを提供しています。それぞれの縫製工場に対して、設備や人員、アイテムごとの金額感、繁閑の目安など300を超える情報をデータベースに格納しています。

工場を検索して絞り込み
(工場名や取引先は非公開のため消してあります)

いくつかの工場を並べて比較
弊社コンシェルジュはこれらのデータを使ってご相談いただいたアイテムごとに最適な工場に問い合わせを行います。
2018年11月20日火曜日
独自マークアップ言語によるケアラベル(品質表示ネーム)エディター
こんにちは、シタテルの茨木です。
衣服には必ずケアラベル(品質表示ネーム)というものがついています。洗濯の方法などが書いてあるアレですね。
凝ったデザインにすることもありますが、大体はパターンが決まっています。
今回は、独自のマークアップ言語でケアラベルを作れるようにしてみた、という内容です。
完成イメージ
- 中央の入力欄で独自マークアップを入力すると、左にケアラベルがリアルタイムプレビューされる
- 画像としてケアラベルをダウンロードできる
- 右欄でCSSによるデザイン微調整が可能(おまけ)
ソースコード
https://github.com/tibaraki/care-label
目次
- マークアップをパースして配列に
- 配列からDOMを描画
- CSSを適用
マークアップをパースして配列に
本件の肝です。
パーサコンビネータparsimmonを使用します。
パーサコンビネータは文法要素の組み合わせで言語(文書)構造を定義していきます。
たとえば正規表現も文書構造を定義するものですが、正規表現は再帰が表現できないという限界があります。
今回使うようなパーサは再帰を記述できるので、理論上あらゆるプログラミング言語の文法チェックが可能です。
今回はこれをつかって、独自マークアップ言語の文法を定義してみます。
下記は入力例&出力例&パーサの本体です。
parseにマークアップを投げるとパース後の配列を返してくれます。
@id1
aaa
@id2
ccc
@id2-1
ddd
eee
@id2-1-1
xxx
fff
@id2-2
ggg
hhh
[
[
"@id1",
[
[
"aaa"
]
]
],
[
"@id2",
[
[
"ccc"
],
[
"@id2-1",
[
[
"ddd"
],
[
"eee"
],
[
"@id2-1-1",
[
[
"xxx"
]
]
],
[
"fff"
]
]
],
[
"@id2-2",
[
[
"ggg"
],
[
"hhh"
]
]
]
]
]
]
import {regex, string, lazy, seq} from 'parsimmon'
function lexeme(p) { return p.skip(regex(/[ \n]*/)) }
const lparen = lexeme(string('{'))
const rparen = lexeme(string('}'))
const elem = lazy('', () => { return block.or(line) })
const id = lexeme(regex(/@[\w-]*/i))
const atom = regex(/[^\{\}\n ]+/).skip(regex(/ */))
const line = regex(/[\n ]*/).then(atom.many()).skip(regex(/\n+/))
const block = regex(/[\n ]*/).then(seq(id, lparen.then(elem.many()).skip(rparen)))
const root = block.many()
export default {
preserve(string) {
let value = ''
let level = 0
string.split(/\r\n|\r|\n/).forEach((line) => {
const indent = Math.floor(line.match(/^ */)[0].length / 2)
if (level < indent) {
value += "{".repeat(indent - level) + "\n" + line + "\n"
} else if (level > indent) {
value += "}".repeat(level - indent) + "\n" + line + "\n"
} else {
value += line + "\n"
}
level = indent
})
value += "}".repeat(level)
return value
},
parse(string) {
return root.parse(this.preserve(string)).value
}
}
文書構造の定義
jsの前半部、constの並ぶ箇所は文書構造の定義です。
意味としては下記のような感じです。
elemとblockが相互参照して再帰しているのがわかるかと思います。
const elem = lazy('', () => { return block.or(line) })
elemはblockまたはlineで構成される
const block = regex(/[\n ]*/).then(seq(id, lparen.then(elem.many()).skip(rparen)))
blockはidで始まり、lparen{
とrparen}
で囲まれた複数個のelemで構成される
preserve
preserveは前処理です。
文書構造の定義でしれっと{}
でブロックを定義していましたが、今回作りたいマークアップはインデントでネストを表現する方式なので、前処理でインデントを{}
に変換しています。
ここもパーサでできればカッコいいのですが、うまいやり方は思いつきませんでした。pythonとかどうしてるんですかね?
配列からDOMを描画
vueで書きます。パース後の配列(persed)とバインドしておけば、リアルタイムに再描画されて楽です。
div#view(:style="viewsize" ref="view")
div(v-for="elem in parsed")
div(v-if="check(elem, /^@mixings/)" :class="classname(elem)")
table
tr(v-for="mixing in take(elem)")
td(v-for="i in columns(take(elem))") {{ mixing[i-1] || "" }}
div(v-else-if="check(elem, /^@marks/)" :class="classname(elem)")
div(v-for="mark in take(elem)")
img(v-if="isValidMarkId(mark[0])" :src="`/img/${mark[0]}.jpg`")
div(v-else-if="check(elem, /^@/)" :class="classname(elem)")
div(v-for="e in take(elem)") {{ e.join(' ') }}
基本的には@hogeをそのままcssのclass(.hoge)として適用し、cssでデザインを定義していく戦略なので、あまり複雑なことはやりません。
ただし、@marksは画像(洗濯マーク)に差し替える必要があるのと、@mixingsはtableでレイアウトしたかったので、vue内で特別扱いしてあげます。
CSSの適用
CSSも書き直したらリアルタイムに反映されてほしいので、更新時に動的に差し替えに行きます。
applyStyle() {
const old = document.getElementById('inserted-style')
old && old.parentNode.removeChild(old)
const obj = document.createElement('style')
obj.setAttribute('id', 'inserted-style')
obj.appendChild(document.createTextNode(this.style))
document.getElementsByTagName('head')[0].appendChild(obj)
},
あまりキレイじゃないですが、head要素に無理やり差し込みます。
その他
HTMLからの画像化は、下記ライブラリを使用しています。
https://github.com/tsayen/dom-to-image
どうもフォントまわりの挙動が怪しく、OS/ブラウザによっては画像出力が上手くいかない場合があります。
まとめ
一通り作ってから、yamlでも良かったのでは、とちょっと思ってしまいました。
とはいえ、非エンジニアにはyamlも辛いでしょうし、目的特化して打鍵の少ない文法を定義したい、というところに独自マークアップの需要はあるかもしれません。WYSIWYGに発展できたりするといいですね。
ご参考になれば幸いです。
2018年11月6日火曜日
DXF TIIPビューワ
こんにちは!
シタテル株式会社CTOの和泉です。
シタテルではユーザー様からの相談を相談をお受けして、衣服の生産管理を行っています。
その際、衣服のパターンデータをいただきますが、このパターンデータはDXFという形式のファイルで送られてきます。
正確にはDXFを拡張したアパレルCADデータ互換のTIIP規約としてフォーマットが規定されています。
このファイルは通常、専用のソフトウェアを使って編集、閲覧するのですが、
「ブラウザ上でユーザー様とメッセージのやり取りをしている中でいちいちダウンロードして確認してというのは効率が良くないから、ブラウザで見たい!」
という現場からの要望がありました。
ということで、DXF TIIPのファイルをブラウザ上で閲覧できる簡易ビューワーを作りました!(1年ぐらい前に)
突貫工事で作ったのであまりできはよくないですが、ザックリとファイルの中身を 見られるので重宝していただいてます。
シタテルの SCSやマイオペレーター、マイアトリエにも組み込まれています。スマホでも見られるので外出先でちょっと確認したいときにも便利です。
フォーマットが見つからなかった、、、
実装するときにファイルフォーマットをダウンロードしよう思ったのですが、ちょっと探しただけでは見つけられず。
いや、きっと私の探し方が悪かっただけだと思うんですが、、、
結果、バイナリエディタとにらめっこしながらなんとなくで作りました。
TIIPはDXFのコメントエントリ(用語適当です、ごめんなさい)の部分を拡張してあるフォーマットだったので、なんとなくで読み取ることができました。
DXFの読み取り
DXF自体の読み込みには three-dxf を利用しました。
そのままだと読み取りできない部分があったのでもとのリポジトリをフォークして少し編集しています。
おわりに
今回のDXFの話はちょっとした部分を便利にしただけですが、 シタテルではこのように現場の声を毎日聞きながらエンジニアが開発を行うことで、現場にフィットした機能を作り、効率性を高めることで多くの相談をすばやく処理して、ユーザー様の衣服づくりをサポートしています。