astamuse Lab

astamuse Labとは、アスタミューゼのエンジニアとデザイナーのブログです。アスタミューゼの事業・サービスを支えている知識と舞台裏の今を発信しています。

どこまでも迷走を続ける採用サイト【後編】

f:id:astamuse:20170201110635p:plain

採用サイト制作が始まってから久しいですが、先日(だいぶ前)ようやく公開の運びとなりました…!!!

recruit.astamuse.co.jp

取りあえずは一区切りついてよかったなぁ。
前2回のエントリでは「採用サイト作る作る詐欺」になっており、非常に心苦しくありました…
ということで、本エントリではそんな心苦しさを織り交ぜつつ、制作過程の振り返り及び今後の改修についてアレコレまとめました。

コンテンツ制作

1. どんなコンテンツを作るべきか?

まず始めに、サイトを構築する上で「アスタミューゼに入社することのメリットは何か?」という観点から最適なコンテンツの洗い出しを行いました。

前々回のエントリでも少し触れましたが、話し合いの結果

  • 無理な残業が少ない
  • 多用な働き方ができる
  • 勉強しやすい環境である

といった事が挙げられました。

また、上記を踏まえ「入社後の自分」をイメージしやすくするにはどうすれば良いか?という点について考えたところ

  • 自分の現状と比較できるコンテンツ
  • 社内がわかるようなコンテンツ

を作ることで入社後のイメージが具体的になり、より興味をもってもらうきっかけになるのではないかという結論に至りました。

次に「入社後の具体的なイメージ」を持って貰うためにはどのようなコンテンツをべきか? を検討したところ

  • 社員の1日
  • 前職との比較
  • 社内動画

の3つのコンテンツを作ることに決めました。


2. コンテンツ制作過程

2-1.動画

まず「社内がわかるようなコンテンツ」、といったらもう社内の様子を見て貰うのが一番手っ取り早いしウソがありません(演出されていたら別ですが…)。
社内の様子を見てもらえば出社時間から帰社時間、それぞれの仕事の仕方がわかりますし、イメージがしやすくなるでしょう。

しかしながら、ここで1つ問題が。

えー、まぁ何て言うか、正直な話、

デスク周りがお世辞にも綺麗とは言い難い(引越をしたばかりだったので)。

果たしてこれを世に送り出していいものか?

わたしたちは悩みました。

「もうちょっと片付いてからにしよう」

そう結論づけたために現在はちょっとフィルターを掛けてお送りしています。
今後、美しい感じで撮影できたら差し替えたい。
是非ともそうしたい。

もしイイ感じの動画がはまっていたら「ああ、ようやく片付いたんだなぁ…」と思って下さい。


2-2.社員の1日

弊社のデザイン・開発部はフレックス制ということもあって人によって出社・帰社の時間にかなり差があり、また自分に合ったペースで仕事ができる環境なので働き方も様々です。
なのでこちらのコンテンツを参考にしていただくことで「今後一緒に働く人かもしれない人がどんな働き方をしているのか?」「自分のペースでもやれそうかどうか?」など、イメージがし易くなると思います。

しかしながら、またここで1つ問題が。

数名にアンケートを採ってみたら

帰りがみんな揃って定時上がり。

こんな示しを合わせたように定時帰りだと、見ている人にウソだと思われるのでは…?

わたしたちは悩みました。

「でも本当だから仕方ない」

そう結論づけたために現在のコンテンツではみんなは仲良く定時帰りとなっています。
もうちょっとアクロバティックな出退勤の人にアンケートをお願いしてコンテンツを追加したい。
是非ともそうしたい。

コチラのコンテンツは今後もアンケートを採って少しずつ増やしていく予定ですので乞うご期待、です。


2-3.前職との比較

さらにアスタミューゼでの働き方をより具体的にイメージして頂くには、見ている方の現在と弊社を照らし合わせるコンテンツがあると良いのではないかと考えました。
デザイン・開発部の全員を対象にアンケートを採ったのですが、無記名で回答して貰ったので個人を特定されるプレッシャーもない結果だと思います。

しかしながら、またしてもここで1つ問題が、

特になかった。

よかったぁ…


全体の振り返り

今後のために制作を振り返って、良かった点/悪かった点についてまとめました。

良かった点/タメになった点

  • 意志決定が早い(方向性が決めやすい・共通認識が持ちやすい)
  • 自分たちの裁量で好きなように作れた(いつもと違うことができる)
  • 通常業務より広範囲に渡って考えることができる

弊社では通常、デザイン・フロントエンド・開発からそれぞれ1~2人が各プロジェクトに割り振られ、総勢5~7人のチーム体制でサービス運営をしています。
今回の採用サイトの制作・運営はいつもの半分、3人体制でした。
人数が少ない分、スピード感が生まれ、サクサク進められる印象を持ちました。
週に1度のMTGも少人数なので予定を確保しやすく、全体タスクの分量や割り振り、進捗確認もしやすいです。

また、裁量がコチラに任せられているということはモチベーションにも繋がりますし、どうやって流入を増やすか、CVさせるか、どう計測するか、その他諸々、コンテンツを含む全体の流れをより意識した制作ができたと思います。

良くなかった点/苦労した点

  • 期限を明確に決められなかった/想定が甘かった
  • プロジェクトに対する積極性

やはり期限を明確に決められなかったというのは個人的には痛かったと感じました。
期限もなんとなく、MTGの流れで「これくらいに出来たら(・∀・)イイネ!!」くらいの軽さだったのが良くなかったかなぁと今となっては反省です。
また、実際には想定していたよりも実装コストが高かったことと、タスクの兼ね合いでこちらの制作にまで手が回らないメンバーもいたため、途中でスケジュールを組み直して期限を切り直すなどの措置が必要だったと思います。

部長からは「きちんとしたコンテンツを目指すためであれば、多少の遅れはやぶさかではない」とのお達しがあったのですが、それでもあと少しリリースタイミングを意識できれば早めに手を打てたのかも知れない、と思いました。

また、上記と共通する話ではありますが、プロジェクトを進める上で3人のうち3人ともぐいぐいと引っ張るタイプではなかったため、今ひとつ積極性・コミットする力が足りなかったのかもしれないと思い至りました。
なので今後は(一部)ぐいぐいやって行きたいなぁ…となんとなく考えています。

今後の予定

今後の予定としましては、個人的にはPC版のデザインを綺麗にしたいのです。
元々、スマホ版からイイ感じに作っていきましょう、というところから始まったのでちょっとPC版のデザインが今ひとつ。
そこをまずキリッとさせたいな、というのが個人目標。

また、流入経路の確保がまだまだなので、そこら辺が当面の課題です。
ついでに申しますと、弊社Twitterアカウントなどがあるのですが、全然フォローされてない。

astamuse Lab (@astamuseLab) | Twitter

悲しみに暮れています。
これもどうにかした方が良いかなぁって、考えたり、考えなかったり…

もし良い案をお持ちの方、いらっしゃいましたらどうぞコチラからご入社ください。
お待ち申し上げております。

Spark 2.0を使ってみた

山縣です。

新年あけましておめでとうございます。

本年もよろしくお願いいたします。

 去年弊社の福田が CDH のアップグレードをしてくれてSpark が1.6系になるとともに、並行してSparkの2.0が使えるようになりました。(Spark2.0の導入については福田の記事をどうぞ→ もう待てない、Spark2.0の導入と実践 - astamuse Lab)

 現在弊社の環境でインストールされている Spark2.0 はまだベータということもあり、当初は少し触ってみる程度に留めようと考えていました。しかし1系のSparkが1.5→1.6に上がったことで問題に遭遇したこともあり、いくつかのバッチは2.0で動かしています。

 今回は1.6や2.0を触っていて遭遇した問題や気になった点などについて書いていきたいと思います。

Spark 1.6 と Tungsten

 社内のSparkが1.6系にバージョンアップされ、自分のジョブの一つが途中で abort してしまう現象が起きてしまいました。ログを見ると複数のDataFrameをJOINする処理で OutOfMemory が出ています。

 原因が分からずいろいろ調査をしていたのですが、改めてログを見直すとTungsten が有効になっていることに気が付きました。

 実は自分の実行している Job では今まで以下のパラメータ指定で Tungsten を無効にしているはずでした。

--conf spark.sql.tungsten.enabled=false

 Tungsten は Spark 1.4 から導入されたSpark の性能を上げるための仕組みです。社内のSpark が 1.3系から1.5系に上がったときに、この Tungsten が原因と思われるエラーが起きたため、上記パラメータで Tungsten を無効にすることで回避していました。

 もしやと思ってSpark 1.6 のリリースノートを見てみると以下のような記述がありました。

The flag (spark.sql.tungsten.enabled) that turns off Tungsten mode and code generation has been removed. Tungsten mode and code generation are always enabled (SPARK-11644).

 

Tungstenモードとコード生成を無効にするフラグ spark.sql.tungsten.enabled は削除されました。Tungstenモードとコード生成は常に有効となります。(SPARK-11644)

 つまり Tungsten は1.6から常に有効で無効化することができなくなってしまったようです。

 その後エラーを回避するためパラメータの調整などを試みましたがうまくいかず、最終手段として Executor のメモリ量を多くすることで何とか回避はできました。

 とりあえず回避はできましたが、このやり方ではデータ量が増えたり、処理がより複雑になったりしたときに、問題が再発する恐れがあります。そこで試しにこのバッチを2.0へ移行してみました。

Spark 2.0 への書き換え

 1系から2系へのメジャーバージョンアップということで当初は結構大変かと身構えましたが、やってみると意外と問題は少なく小規模な修正で済みました。具体的には以下のような修正をしました。

  • Scala のバージョン変更…Scala が 2.10系から2.11系に変わりました。
  • Spark ライブラリの変更…build.sbt で指定するSpark ライブラリのバージョンをSpark2.0のものに変更しました。
  • SparkSession 対応…Sparkのプログラミングをする上でのエントリポイントとして新しくSparkSession が導入されました。spark-shellにおいてsqlContext:SqlContext が無くなったので spark:SparkSession からSqlContext を取るようにしました。
  • spark-csv の置き換え … CSVファイルの入出力をするためのライブラリspark-csv の機能が2.0から標準で含まれるようになったので spark-csv を依存ライブラリから外しました。合わせて csv の入出力周りの処理を修正しました。
  • registerTempTable … Dataframe を SparkSQLから使うときにDataFrameのregisterTempTable メソッドでテーブルとして登録します。2.0からはこのメソッドがDeprecatedになったので createOrReplaceTempView に変えました。 (注 2.0 からDataFrameクラスは無くなりDataset になったので正確には Dataset のメソッドになります)

 つらつらと書いてみましたが、どれも大した変更ではなく、予想していたよりも楽に移行ができました。実際にビルドして実行してみると1.6で落ちていたジョブは無事に完了することができました。

 ただジョブそのものはtaskのエラーもなく進んでいるのに、driverのコンソールにエラーログが大量に表示されたり、途中で止まった処理の情報がコンソールに残ってしまったり、ベータだからか、まだ挙動が少し安定していない雰囲気もありました。そういうこともあり2.0への移行は様子を見ながら必要に応じてと考えています。

 と、これを書いている時点で、Cloudera社から Spark 2.0 Release1 が出ていることに気が付きました。いずれ社内にも導入されると期待しています。

Dataset について

 Dataset はSpark 1.6から導入されたSparkの新しいデータ形式です。Dataset は従来のDataFrame を拡張し、型パラメータを持ちます。

scala> import org.apache.spark.sql.{Dataset,Row}

scala> case class Msg(id:Int, msg:String)

scala> val ds:Dataset[Msg] = Seq(Msg(1, "I have a pen."),Msg(2, "I have an apple."),Msg(3, "Do you have a pen?")).toDS()
ds: org.apache.spark.sql.Dataset[Msg] = [id: int, msg: string]

 型パラメータを持つことでタイプセーフなプログラミングが可能となりました。従来 DataFrame を RDDに変換したりmap()foreach()などで処理する場合、DataFrame の各レコードは Row というクラスで表され、カラムからデータを取り出すときはRowクラスの getAs[T]()getString(), getInt() などのメソッドでデータを変換して取り出す必要がありました。

 Dataset では型パラメータとしてcase class などを指定することが可能で、各行を取り出すとき case class のインスタンスとして取り出すことが可能です。

scala> ds.map(x => x.msg).collect //x の型は Msg
res11: Array[String] = Array(I have a pen., I have an apple., Do you have a pen?)

 DataFrame では、例えばテーブルAのDataframe (dfA)もテーブルBのDataframe(dfB) も同じDataFrame のインスタンスなので、dfAに対して処理する関数を間違えてdfBに適用したとしてもコンパイルエラーになりません。

 しかしDataset を使えば型パラメータが違うのでコンパイル時にバグを発見することができます。下記のように Dataset[Msg] に対して処理する関数procDSMsg()に Dataset[Person] を渡すと引数の型が違うのでエラーとなります。

scala> case class Person(id:Int, name:String)

scala> val ds2 = Seq(Person(1, "Taro"), Person(2, "Jiro"), Person(3, "Subro")).toDS
ds2: org.apache.spark.sql.Dataset[Person] = [id: int, name: string] 

scala> def procDSMsg(ds:Dataset[Msg]):Unit = println("Hello")

scala> procDSMsg(ds)
Hello

scala> procDSMsg(ds2) // 引数の型が違うのでエラーとなる

<console>:35: error: type mismatch;
found : org.apache.spark.sql.Dataset[Person]
required: org.apache.spark.sql.Dataset[Msg]
procDSMsg(ds2)

 なお 2.0 からは 従来までのDataFrame はクラスとしては無くなり、Dataset に統合されました。DataFrame は以下の通り定義されています。

type DataFrame = Dataset[Row]

 つまりDataFrame は Rowを型パラメータとしてもつDatasetということになります。

Dataset用のCase classを半自動生成する

 Dataset によりタイプセーフなプログラミングが可能になりましたが、一方でこの case class 誰が作るの?テーブルごとに作るのめんどくさいので自動生成したい、と思うのは自然なことかと思います。

 ということでどうしようかなと思って考えたのですが、はじめに思い付いたのはDBライブラリのクラスの自動生成機能を使うことです。

 普段利用させていただいている ScalikeJDBC にも sbt のプラグインとしてclassを生成する scalikejdbcGen があります。試しに一つのテーブル用のclassを生成し、必要なところだけを抜き出して試してみました。しかし結果はうまくいきませんでした。理由はscalaikejdbcGen ではDBのカラム名の記法が sneak(例 abc_def_ghi) の場合に、対応するメンバ変数の名前を camel (例 abcDefGhi) にしてくれるのですが、Dataset 側でそういう変換には対応してくれないからでした。また、よくよく考えるとこの方法だと JDBCに対応したRDBMS以外のデータソースに対応できないという問題がありました。

 そこで次に思い付いたのが DataFrame/Datasetが保有しているスキーマ情報から生成することです。

 DataFrame/Dataset は schema:StructType に、DataFrame/Dataset を構成するカラム情報を保有しています。 StructType は各カラムのカラム名やデータ型などの情報を表すStructField の配列(Array)をデータとして持っていますのでこのデータを使えば case class のメンバーを定義できるはずです。

scala> ds.schema.fields.foreach(println)

StructField(id,IntegerType,false)
StructField(msg,StringType,true)

 そこで、case class を生成する Schema2CaseClass というクラスを作ってみました。

 試すにはリンク先のソースコードをコピーして spark-shell の paste で実行します。

scala> :paste
// Entering paste mode (ctrl-D to finish)
import org.apache.spark.sql.types._

class Schema2CaseClass {
...
...
// Exiting paste mode, now interpreting.

import org.apache.spark.sql.types._
defined class Schema2CaseClass

使い方は以下のようになります。

scala> val df = Seq(Msg(1, "I have a pen."),Msg(2, "I have an apple."),Msg(3, "Do you have a pen?")).toDF()

scala> val s2cc = new Schema2CaseClass
scala> import s2cc.implicits._

scala> println(s2cc.schemaToCaseClass(df.schema, "Msg2"))
case class Msg2 (
    id:Int,
    msg:Option[String]
)

 上記のようにDataFrameのスキーマ情報から Msg2 という case class を生成したので実際に試してみます。

scala> :paste
// Entering paste mode (ctrl-D to finish)

case class Msg2 (
    id:Int,
    msg:Option[String]
)

// Exiting paste mode, now interpreting.

defined class Msg2

scala> val ds = df.as[Msg2]
ds: org.apache.spark.sql.Dataset[Msg2] = [id: int, msg: string]

scala> ds.collect
res14: Array[Msg2] = Array(Msg2(1,Some(I have a pen.)), Msg2(2,Some(I have an apple.)), Msg2(3,Some(Do you have a pen?)))

 上記のように Msg2 を 型パラメータとして指定してDataset を作ることができました。

 生成されたMsg2 は msg がOption[String]型になっています。これは元のDataFrameのスキーマ定義で nullable が設定されているからです。

 Optionが適切に処理されるのか確認してみます。

scala> val df = Seq(Msg(1, "I have a pen."),Msg(2, "I have an apple."),Msg(3, null)).toDF()
df: org.apache.spark.sql.DataFrame = [id: int, msg: string]

scala> val ds = df.as[Msg2]
ds: org.apache.spark.sql.Dataset[Msg2] = [id: int, msg: string]

scala> ds.collect
res16: Array[Msg2] = Array(Msg2(1,Some(I have a pen.)), Msg2(2,Some(I have an apple.)), Msg2(3,None))

scala> ds.filter(_.msg.isDefined).collect
res17: Array[Msg2] = Array(Msg2(1,Some(I have a pen.)), Msg2(2,Some(I have an apple.)))

 上記のように元データが nullの場合は None と変換され、filterなどで処理ができます。

DatasetとDataFrame の関係

 Datasetが出たので今後は DataFrame から Dataset に移行が進んでDataFrameは使われなくなっていくのかなと思っていました。しかし実際に使ってみると DataFrame も今後も使われていくという印象を受けました。 Dataset は確かにタイプセーフで良いのですが、例えばJoin した結果などで、それ自体が処理を分割するための中間的なデータであったり、一時的にしか使われないようなデータに対して、わざわざ case class を定義する必要は無いのではと思います。 また SparkSession.sql は DataFrame を返します。 2.0からDataFrameをDataset[Row] としたことでDataFrame/Dataset間がシームレスに使えるようになったことも合わせると今後ともDataFrameとDataset をうまく使い分けていくのが良いのかなと考えています。

終わりに

 2016年の Spark は2.0のメジャーバージョンアップも含めて非常に高速に進化していった印象を受けました。年末にはすでに2.1.0も出るなど相変わらずバージョンアップが激しいです。

 今年もどのように進化していくのか楽しみなプロダクトです。

特許とその制度について 特許出願および実用新案登録出願

お久しぶりです。主に特許関連のデータ処理を担当しているBTと申します。 前回、特許及び実用新案の概要についてご説明させて頂きましたが、今回は日本国内における「特許出願」および「実用新案登録出願」についてご説明いたします。 宜しくお願いいたします。

特許出願

まずは、出願から特許をうけるまでの基本的な流れは、以下の図のようになります。

f:id:astamuse:20170118114118j:plain

このうち、「特許出願」について説明します。

発明に対して日本国で特許を受けるためには、特許庁に「特許出願」をする必要があります。その際に以下の書類をそろえて提出する必要があります。

f:id:astamuse:20170118114126j:plain

それぞれの書類について説明していきます。

「願書」

「願書」には発明の内容そのものではなく、発明者の住所と氏名、出願人の住所又は居所と氏名又は名称、添付物件の目録(この「願書」以外の書類の一覧)などを記載します。さらに出願人が外国人や日本に営業所を持たない外国の企業の場合は、代理人(要は日本の弁理士)の住所と氏名も記載することが必要です。

「明細書」

「明細書」には、「発明の詳細な説明」をもうけて、特許を受けようとする発明やその他の発明(特許を受けるまでは考えていないが、特許を受けようとする発明と同時にした関連する発明など)について説明を記載します。その発明の属する技術分野における通常の知識を有する者(つまり、その分野に従事している技術者など)が「明細書」を読んで発明を実施(発明品の製造など)出来る程度に明確かつ十分な内容を記載することが必要とされ、読んでも理解不能であるとか、発明を再現できないような曖昧な記載は認められません。記載する内容としては、発明に到る課題、課題を解決するための手段、発明の効果、発明の具体例である実施例、関連発明(「特許出願」の時に把握している関連する他の発明)などがあり、後で述べる「特許請求の範囲」とは異なり一般的な文章で記載します。

「特許請求の範囲」

「特許請求の範囲」には、特許を受けたい発明について、その発明を特定する為に必要な全ての事項を記載する必要があります。発明が複数ある場合は、「請求項1、請求項2、・・・」のように発明ごとに分けて記載することができますが、一つの「特許出願」の「特許請求の範囲」に含まれる複数の発明は、一定の技術的関係を有する必要があります。これは一つの出願にのなかに無関係な発明が複数含まれていると、その出願の審査や第三者が出願内容を参照する際に混乱をきたすためです。

「特許請求の範囲」は、独特の文体で書かれます(中にはそうなっていない出願もありますが、それは弁理士や特許事務所を代理人とせずに直接出願しているか、弁理士や特許事務所のチェックを受けていない場合が多いようです)。一つの請求項は一つの文で記載するため、構成要素が多い発明や手順などが多い発明の場合、一つの請求項が延々と数ページにわたって続くこともあります。一般的にはとても読みにくい文体のために、特許に関する文書を読んだり書いたりするのが苦手という人も多いと思います。「特許請求の範囲」は、もし特許を受けた場合にその特許の権利範囲を決めるための法的文書としての性質を持つことから、曖昧さを排除して権利範囲を明確にする法律的に有効な文章であることが望ましく、このような文体にせざるおえないという事情があります。

また、「特許請求の範囲」はの記載には、以下の点を守る必要があります。 * 請求項に記載した特許を受けようとする発明が「明細書」の「発明の詳細な説明」に記載されている必要があります。発明について「特許請求の範囲」に記載するだけではなく、「明細書」に方にも記載(通常はより詳細な説明と共に)すること * 特許を受けようとする発明が明確に記載されていること * 請求項ごとの特許を受けようとする発明の記載が簡潔であること * その他経済産業省令で定めるところにより記載されていること

「図面」

「図面」は、「特許出願」における他の出願書類と違って必ず必要というわけではありません。図がないと説明が難しい物の発明では無く図がなくても説明が可能な方法の発明や、化学など発明の分野によっては図をそれほど必要としない分野などで、「図面」を添付しない出願が多いことがありますが、特許を受けようする発明について解りやすく(特に「特許出願」を審査する審査官により深く理解してもらうために)図を用いることが望ましいため、一般的には「図面」が無い出願はほとんどありません。

「要約書」

「要約書」は、「明細書」、「特許請求の範囲」、「図面」に記載した発明の概要を記載したものです。これは、「特許出願」の数の増大や技術の高度化・複雑化によって情報へのアクセスが困難になっている状況から、出願した内容へのアクセスを容易にして利便性を高めるために必要とされているものです。その性質上、「要約書」の記載内容が「明細書」、「特許請求の範囲」、「図面」に記載した発明とかけ離れていたり、不明瞭である場合は、特許庁によって内容が書き換えられることがあります。

その他の申請書など

その他「特許出願」にあたり特例を受けたい場合は、それぞれの特例の申請書を提出する必要があります。特例の内容については次回に説明したいと思います。

実用新案登録出願

まずは、出願から実用新案登録をうけるまでの基本的な流れは、以下の図のようになります。

f:id:astamuse:20170118114131j:plain

このうち、「実用新案登録出願」について説明します。

発明に対して日本国で特許を受けるためには、特許庁に「実用新案登録出願」をする必要があります。その際に以下の書類をそろえて提出する必要があります。

f:id:astamuse:20170118114135j:plain

「実用新案登録出願」の場合に特許庁に提出する書類は、上記の「特許出願」とほとんど(但し、「特許請求の範囲」は「実用新案登録請求の範囲」という書類名に変わります)同じですが、「実用新案登録出願」の場合は「特許出願」で提出が必須では無かった「図面」が必須となっています。これは、実用新案ではその保護対象が物品の形状、構造又は組合せに限定されており、考案(特許における発明に該当)の説明に「図面」が必要とされているためです。

まとめ

以上、特許と実用新案の出願について説明をしてきました。次回は、出願において申請することが出来る特例や通常出願とは違う特殊な出願について見ていきたいと思います。

最後になりましたが、 アスタミューゼでは現在、エンジニア・デザイナーを募集中です。 興味のある方はぜひ 採用サイト からご応募ください。

参考にした資料など

Copyright © astamuse company, ltd. all rights reserved.