AdExchangeとSupply Side Platform(SSP)との違いって何よ?
www.slideshare.net
What is the difference between ad exchanges and supply-side platforms? - Quora
説明に困るAdExchangeとSSPの違い。ポジションレイヤー的な話だとAdExchangeもSSPともにDSPやADNetworkを束ねてPublisher側に広告を流す仕組みであり、この点だけを述べてしまうとほとんど差異が無いように思われる。また現在に至っては使われ方もほとんど同じと言える。(細かい話だとAdExchangeはDSPを束ねてSSPと接続をする場合もあるがここはあまり意識されない。) Quoraを調べていたら2つの違いについて適切な解答があり、最もupvoteが付いているものを読んでみた。結論としては作られた目的が異なる。AdExchangeは薄利なレムナント在庫を自動管理するもの、SSPは単価の高い広告を適切な在庫に流してPublisherの収益最大化を行う。以下は大体の翻訳。
AdExchangeはStock Exchangeを例としたトランザクションを促進する自動化プラットフォームである。用途としては主にレムナント在庫に使われる。レムナント在庫は余剰在庫なので価値がとても低いから人的なリソースを使わずに売りたい。AdExchangeは薄利なので配信量を多くすること頼っている。SSPは供給サイドのサービスでTechnology Platform/Service/Sales(人)のコンポーネントの組み合わせである。彼らは複雑かつExchangeとは取引をしないような大規模なPublisher,DSP,広告主と関係を保っている。Publisherとしては自身のビジネスに専念できるし、扱いたい広告vendorの数を制限できる。(しかし注意しなければいけないこととしてはPublisherはスーパープレミアムな在庫はSSPに開放せず別途持っていてインハウスのSalesチームにBig Brandとして売らせている。) SSPは広告主の沢山の広告をRealTimeBiddingによって提供するようなInterfaceを持ち、Publisher側の個々のadspotの価値を戦略的に最大化させる。SSPはDMPなどのlayerと接続可能なのでAdExchangeより多くのGross Marginを得ている。(注意しなければいけないことはSSPはAdExchangeより様々な面での高いコストを支払っている。)
Defining SSPs, Ad Exchanges And Rubicon Project | AdExchanger
上のリンクはAdExchangeとSSPとRubicon Projectの定義。以下は大体の翻訳
AdExchangeとSSPはかつては異なるものであったが段々と区別が困難になりつつあり、補完的な技術は統合されている。SSPが登場した頃、AdExchangeとSSPの違いはAdtech vendorの間ではとても重要な内容だった。デジタル市場の動きがとても速いという理由もあって特徴的な名前付けをする必要があった。SSPとAdExchangeの統合は新しいことではなく、GoogleはSSPのAdmeldとDoubleClick AdExchangeを統合している。歴史的な違いとしてはAdExchangeはトランザクションを有効にする一方で、SSPはPublisher側に価格調整機能などの多くのToolを提供してAdExchangeと同じことを行う。長い間様々なPlayerを見ていると、特定の市場空間にいる大体の会社がより優れた顧客サービスをできるようにツールの開発や機能提供を行う。Rubiconは収益最大化を目的としたSSPとしてスタートしたはずが、彼らが十分な在庫を持っていたならば、自然とAdExchangeの機能を統合している。この産業の誰もがmedia platformerであって、全てのPublisherが喜んでいない状況である。SSP,AdExchange,DSPやAdnetworkの統合は周囲(広告主やPublisher)を混乱させる。
Recsys2015で発表されたCriteo社の最新レコメンド情報を読む
www.slideshare.net
criteo社のRecommendation Logic
Criteo社の広告枠への配信に対するCTR/CVR予測モデルの話以外にもRecommendLogicに関する発表がRecsys2015であったので見てみました。一般的なRecommed Systemでは良く見受けられる構成や手法なのでインパクトがある内容ではありませんが共有します。Keywords : Logistic Regression, Hadoop, Memcached, collaboration filterling, Recsys2015,
Criteo社のCTR/CVR予測モデルは下記論文などを参照 Simple and scalable response prediction for display advertising
Criteo社の説明とインフラ
- 2005年に設立、フランスに本社を置き世界に社員1700人(そのうちEnginnerが300人)、100億のUniqUserを持つ。
- 秒間80万のHTTPリクエスト、29000インプレッション、RTBの処理を10ms、Recommendの処理を100msでこなす。
- 3つの大陸にインハウスのデータセンターが7つ。サーバー15000台、ヨーロッパに巨大なHadoop Clusterがある。35PBのBig Dataがある。
Criteo社のデータソース
- 商品カタログデータが広告主ごとに100万ほど。その広告主を10000社持つ。
- インターネットユーザーの行動履歴のイベント数が毎日2B。
- 広告表示やクリックなどのイベントデータが毎日20B。
Recommend Logic
- やりたいこととしてはRecommendの関数にインターネットユーザーを入れた時に一番のオススメ商品が出るようにしたいが、実際のところ複数の商品が出るし、新鮮さも保たなければならない。
- できることとしてはOfflineで事前に商品の推薦を選んでおき、OnlineでRecommendationを決定する。
- Offlineで広告主からインターネットユーザーの行動ログを送ってもらい、商品ランキング/カテゴリ商品ランキングを定め、商品閲覧の共起から類似商品を導き出し、商品購買の共起から補足商品を推薦する。
- インターネットユーザーが商品Xを閲覧した時の推薦する候補は以下のもの
- 閲覧した商品Xそのもの。(直接商品)
- 他のインターネットユーザーが商品Xと閲覧共起しているもの。(類似商品)
- 他のインターネットユーザーが商品Xを閲覧した上で購入したもの。(補足商品)
- 広告主のサイトで最も見られて/買われているもの。(売れ筋商品)
System Overview
- 4時間おきに広告主から商品カタログデータを貰い、Memcachedに入れる。
- 広告主サイト側でのインターネットユーザーのイベントログを送ってもらい、商品アイテム推薦を導き出すためにHadop MapReduceにかける。MapReduceされた推薦データは12時間おきにMemcachedに入れられる。
- 広告表示/クリック/コンバージョンデータを6時間おきに取得し、それを基に予測モデルをHadoop上で更新する。
- Recommendのリクエストが来るたびに、インターネットユーザーのデータを用いて推薦アイテムを商品カタログから参照する。
- 機械学習の予測モデル(おそらくクリックやコンバージョン予測)はLogistic Regressionを利用している。なぜならばLRはスケールするし、速いし、たくさんの特徴を扱うことができる。(bitのhash magicを使うなどして)
- Memcachedに入れられた類似商品、最も見られている商品、最も買われている商品をスコアリングして高いものから推薦する。
Recommend Logicの評価
- オンラインABテストを行う際はインターネットユーザーの50%ずつにそれぞれのモデルを適用するが、これらは以下の理由により煩わしい。
- すぐに多くのお金を失う。
- テストの期間が長くなってしまう。信頼がおける期間は2週間ぐらい必要。
- ソースコードを本番同様にしなければならない。
- インフラ的に高負荷になってしまう。
- 広告主のアカウントがそこまで十分な時間をくれない。
- その代わりとしてOnlineのログを使ったテストフレームワークがある。
- しかし自分たちで管理するデータしかもっていない。(外部データは非常に高価なため)
- それでも私達は完全な間違いをしないことの確信がある。
- Onlineのパフォーマンスを上げるためにOfflineテストの最善策を見つけなければならない。以下は参考論文 Counterfactual Reasoning and Learning Systems
今後のUpdate
- より長期的なUserのProfileデータを活用する。
- より多く/より良い商品情報を利用する。(画像、文脈、NLP)
- 類似商品計算のUpdate。
- 商品スコアリングの結合。
- 私たちはClick予測のデータを公開している。
- 4GB display-click data : Kaggle challenge in 2014 http://bit.ly/1vgw2XC
- 1TB Display-Click data (industry’s largest dataset) : http://bit.ly/1PyH4Vq
- 4 billion of observations
- 156 billion feature-value
- available on Microsoft Azure
- used by edX (UC Berkeley)
アジャイル開発の導入に向けた備忘録
- 作者: 西村直人,永瀬美穂,吉羽龍太郎
- 出版社/メーカー: 翔泳社
- 発売日: 2013/02/13
- メディア: 単行本(ソフトカバー)
- 購入: 5人 クリック: 13回
- この商品を含むブログ (33件) を見る
- 作者: Jonathan Rasmusson
- 出版社/メーカー: オーム社
- 発売日: 2014/06/25
- メディア: Kindle版
- この商品を含むブログ (4件) を見る
SCRUM BOOT CAMP と アジャイルサムライ
このブログも勉強したことの纏めサイト的なものになってしまってきているので、体験談や自分の考えも纏められるようにしていきたいですね、、、。今回はアジャイル開発手法の1つであるSCRUMについて計画,実施,振り返りを行う機会があったので、新しいインプットとしてSCRUM BOOT CAMPとアジャイルサムライを読んでみました。過去に独自色の強いアジャイルを試したことはありましたが、コミュニケーション時間の増加および課題の明確化ができるたものの、アジャイルの学習や進行に費やした時間に対するプロダクト成果まで結びつかず途中で挫折しました。その経験からチームビルドを半ば諦めて個人の力で打開する雰囲気作りをしていた節があったので、これを機にもう一度教科書を読んでから考え直してから実践したいと思います。掲載している内容は自分の言葉で置き換えておりますのでご注意ください。
SCRUM BOOT CAMP
- 事前に全てを予測するのは不可能。そこから計画することはできない。 という事が前提。
- 要求の分析や設計には時間をかけず、重要なものやリスクが高いものから先に作る。
- 要求の実現順、問題点を明確化、もっと良いやり方の適応などを意識する事。
- 開発チームの規模は3〜9人ぐらいがベスト。
- スプリントは短くて1週間、長ければ4週間。週単位で区切る場合が多い。
- スプリントにおいて開発する量は過去の実績値(velocity)を元に予測。
- スプリント計画で合意した内容に全力を尽くすが、完了を約束するわけではない。
- スプリント単位でリリース判断可能なプロダクトが求められる。完了の定義を事前に明確化し、途中での削除は避ける。
- スプリント内では毎日の状況確認。
- スプリント内で出来上がった動作するプロダクトを確認する。スプリントレビューはプロダクトオーナーに対してデモを披露する。
- スプリントの最後に振り返り(スプリントレトロスペクティブ)を行い、もっと成果を出すためのプランを出す。
- プロダクトオーナーとスクラムマスターの兼任は避けたほうが良い。プロダクトオーナーは製品を良くすることを考え、スクラムマスターは開発を円滑に進める。
- プロダクトバックログのためにインセプションデッキを作成する。インセプションデッキのテンプレートは以下にある。
- プロダクトバックログは全員が必ず理解する。
- プロダクトバックログの工数や予算は実際に作業する担当者が見積もる。見積のズレは必ず発生するので、時間をかけずに行う。基準となる作業を見つけて相対的な見積をする。当てずっぽうの見積の中でベストを尽くす。
- スプリント内で実現しようとするプロダクトバックログの量が多いと大変で良くない傾向なので、確実に終わらせる事ができる項目数を定義する。
- デイリースクラムが単純な進捗報告会とならないように、問題を見つけるように工夫する。問題が出てきたらデイリースクラムの後に関係者で集まって対応を考える。
- スプリントレビューではデモを目で見て、本当はどうすべきかを確認する。デモと完了の定義を比較して終わっているかも合わせて確認する。完了の定義はチーム内で合意をしていないといけない。
- 問題に対しては個人でなんとかしようとしがちなので、チーム全員で対応すべき。
- タスクボードを使ってTodo,Doing,Doneを分かりやすくする。
- バーンダウンチャートを使って縦軸に見積もり時間の合計、横軸に営業日を記録し、最終日が0になるような折れ線グラフを作る。これが理想線となり、実際のタスクの実行時間をプロットして理想線と比較する。
- スプリントを旨く回すために自分たちでルールを決めても良い。
- velocityに求められのは安定性。安定していないvelocityは予測に使えない。velocityを上げていくことだけを意識していくと悪影響がでる。
- みんなをサポートしてプロジェクトの目的を達成していくのがスクラムマスターの仕事、サーヴァントリーダシップと呼ばれる。またスクラムマスターはあらゆる妨害を見つけて取り除く。
- 良いスクラムマスターだと妨害リストに50個以上項目を管理している。妨害リストはチームが見えるところに貼っておくと良い。
- プロダクトバックログは日々更新されるべきで、いろいろな意見が集まるように誰でも更新できるようにしておくと良い。プロダクトバックログを観て削除できるものは優先度を下げたり諦めるなどをする。
- 開発メンバーのプロジェクトを進めるスキルがあるかどうかを判断するためにスキルマップを書いてみると良い。開発メンバーの性格や得意/不得意を知った上で各メンバーとこれまでどんなプロジェクトにいたか、仕事で重要視すること、期待されていることは何かを話し合っておくことも重要。
- 失敗を重ねて学んで行く。成長しないスクラムチームではプロジェクトがうまくいかない。失敗を繰り返さないように学んでいく。
- 通常のスプリントの後でリリースの前にリリーススプリントをやることも初期のスクラムチームでは採用される。リリースに必要なことを片付けるための期間。
アジャイルサムライ
- チームの持てる力を最大限に出すことでプロジェクトの成功確率を向上させる。
- 本当に価値があるものに対して集中し、余分なものは捨てて身軽になる。
- アジャイル開発は気弱な人には合わず、隠すことが無い、質の高い仕事へ情熱を持つこと、価値を生み出すことを期待するのであるならアジャイルを採用することによって得ることは多い。
- プロジェクトの3つの真実。1.プロジェクトのスタートでは全ての要求を決めれない、2. 要求はどれも変わる、3. やる内容は時間と資金より常に多い。
- チームを縦割り組織にしないで、役割分担を区別しない、継続的な開発、チームで責任を果たそうとする態度の3つが大事。
- ビジネスを理解しているプロダクトオーナーと開発者は日々一緒に働かなければならない。
- 人に合わせて役割を決めることがチームビルディングのコツ。
- 自分たちで計画を立てることで当事者意識を持たせる。
- “最良のアーキテクチャ・要求・設計は,自己組織的なチームから生み出される”
- 良い組織状態はアジャイルプロジェクトマネージャーが1週間いなくても業務に支障がでないこと。
- 我々は何故ここにいるのか、司令官は何を考えているのかを把握する。
- リスクを全て洗い出す。リスクを分類して解決に取り組む価値のあるリスクを取り除く働きをする。
- 時間と予算は有限、品質は高いものでないといけない、やるべきことが際限ないという4つのフォースがプロジェクトを支配している。
- 文書に頼らず顔を合わせて話してすこともアジャイル開発の原則。
- 良いユーザーストーリの6つの要素。1. 独立(Independent)、2.交渉可能(Negotiable)、3.価値ある(Valuable)、4.見積もり可能(Estimatable)、5.小さい(Small)、6.テスト可能(Testable)。これらの頭文字でINVESTと呼ばれる。
- プロジェクト初期段階での概算見積もりは最大4倍ほどの差があると思っておくこと。
- 人は相対見積もりだとうまくいく。絶対見積だと難しい。仕事の大きさを相対的な数値として捉えられることが重要。大きさを小、中、大の3つに分類し、それぞれ1pt、3pt、5ptなどのように定義する。基準となるストーリーを1つずつ決め、残りのストーリーはそれと同じ基準で当てはめて考える。
- 見積もりを行うときにプランニングポーカーを利用しても良い。チームメンバーの意見を聞く事が可能。
- アジャイルでは計画の見直しとしてリリース期日を延ばすことよりスコープを柔軟に対応することのほうが多い。
- team velocity = 完了したストーリー数 / イテレーション数が通常の計算だが、実際の現場では以下のようになる。team velocity = 完了させたスローリーポイント数 / イテレーション数。
- バーンダウンチャートとバーンアップチャートのどちらかを採用して見える形でプロジェクトへの期待をマネジメントする。
- プロジェクトの途中からアジャイルを導入することも可能。
WORK RULES! はGoogleでの採用やマネジメントが分かる良本
- 作者: ラズロ・ボック,鬼澤 忍,矢羽野 薫
- 出版社/メーカー: 東洋経済新報社
- 発売日: 2015/07/31
- メディア: 単行本
- この商品を含むブログを見る
内容の覚書
Googleにいらっしゃる人事担当/上級副社長?のラズロボックさんが書いた本。最近の僕のtopicsはチームメンバーと自分が同じ方向性で共に成長できるかどうかということ、それに対する経営者やリーダーやマネージャーとしてのあり方というものであり、この本でもその点に注意して読んでみた。訳者の意訳力もあるのか、かなりスラスラ読める本なのでお薦め。忙しい人は冒頭と14章だけ読んでみると良いかもしれない。最高の人材(人財)を採用しチームとして機能させるための並々ならぬ努力が読み取れる。
- マネージャーはチームに奉仕し、現場の障害を取り除いてチームが躍動することに集中する。
- 最高の創業者は他の創業者が自分と並び立つ余地を生み出す。会社員でも自分を創業者とみなし行動する。
- ミッションが個人の仕事に影響を与えるのは事業目的ではなく、道徳だからである。歴史上大きな力を振る舞った運動は道徳的な動機を持っていた。ミッションへの信頼が共有されているためほとんどのグーグラーは結束している。
- 社員の発言や意思決定を重要視する。(余談:中国での検索結果検閲の判断についても社員からの意見に耳を傾けた。www.google.cnはサービスを閉じたが、www.google.hkは継続している)
- 一般的な企業の場合は社員教育に資金や時間など力を注ぐが、Googleの場合は上位10%に入る求職者を採用し、その後の手間をかけずに済ませる。採用費用は平均的な企業の2倍ほど。
- 何らかの点で自分より優れた人だけを雇う。
- アイビーリーグの平均/平均以上の卒業生より、州立学校をトップで卒業した聡明で勤勉な学生を採用したい。過去にやり遂げたことをに比べてどこの大学を出たかはさほど重要ではない。
- 単純に賢いからという理由で採用するのではなく、一緒に働く全ての人を成功に導いてくれる人を採用する。
- 本当に優秀な人は仕事を探していない、在籍している会社に満足しているし十分な報酬を得ている。そこでテクノロジーの力を利用して最高の人材を探し出すヘッドハンティングを行う。(gHire)
- 面接者は最初の5分間で評価を行い、それ以降の時間は最初の評価を確認するための時間として費やす。
- 人の職務能力を予測するための最善の指標は日々の業務内容と似た仕事(ワークサンプルテスト)を与えること。
- 自己複製採用マシーンを作成するための4つの事。1. 採用候補者の人材の質/基準を高く設定する。2. 社員が自分自身で採用候補者を見つける。3. 採用候補者を客観的に評価する。4. 採用候補者に入社すべき理由を伝える。
- 人材の質を確保するためにはあらゆる圧力には屈せず闘う。
- 「権力は腐敗する。絶対的な権力は絶対腐敗する。」権力を持つものは持たないものより道徳的規範を持たなければならない。
- マネージャーの権限が少なければ少ないほどチームが取り入れるべき自由裁量の余地は増えることになる。
- マイクロマネジメントはミスマネジメント。
- 常識はずれの野心的目標を設定して失敗したとしても、きっと何か素晴らしいことをやり遂げたことなる。
- Googleでは各個人のOKRを確認することができる。
- 人間の本質を変えるためのマネジメントではなく、仕事を変えるためには何が必要かということ。
- マネージャーの仕事は人々が働く意味を見つけることをサポートすること。
- スター社員を分析して他の社員より成功している理由を見つける。
- 業績が低い人への思いやりも忘れてはいけない。採用のプロセスが適切ならその人への役割の与え方が間違っている。学習を支援して新しい役割を見つける。
- 人生で最低/最高の瞬間に出くわしたとしても自分より寛大な組織が後ろに居てくれると分かるだけで慰めになる。
- 評価決定前にマネージャー陣が集まって部下の最終評価を検討するキャリブレーションがあるので公平な評価が可能。
- 社員の評価においてボトムテールの人々の改善に投資をすることはチームの改善に繋がる。それでも業績を上げない場合は退職して別の会社で成功を収める。
- パフォーマンスの高さは状況に依存するので、他の職場でうまくいっていても新しい環境でうまくいくとは限らない。
- エンジニアは管理されることを嫌う。彼らはマネージャーの事が嫌いだし、マネージャーになりたくないと思っていことも確か。
- 優れたマネージャーの8つの属性。1. 良いコーチであること。2. マイクロマネジメントをしないこと。3. メンバーの成功や満足度に関心を示すこと。4. 生産性/成果思考であること。5. 話を聞いて、情報を共有すること。6. チームメンバーのキャリア開発を支援。7. 明確な構成と戦略を持つこと。8. チームに対して助言できる技術スキルを持っていること。この中でも技術的な専門知識は重要度が一番低い。
- ある分野で専門的な知識を得るためには1万時間が必要。ただし1万時間の長さではなく、どう使うかが重要。少しでも確実にのこるものを学ばせるほうが賢い投資。
- フィードバックのない反復はモチベーションが向上しないし進歩しない。組織やチームの学習効率をあげる方法の一つとして学習するスキルを細かい要素に分けて具体的なフィードバックに返すこと。更には学習がもたらした結果を測定する。
- 最善の学習方法は教えること。
- 外発的動機づけの報酬体系を洗練させ、4つの原則が生まれる。1, 報酬は不公平。2. 報酬ではなく成果を称える。3. 愛を伝え合う環境づくり。4. 思慮深い失敗に報いる。
- 最も優秀な人でも失敗するので、失敗にどう対応するかが問題になる。またリスクを賞賛しないと誰もリスクを取らなくなる。
- 社会的構造の隙間にいる人ほど素晴らしいアイディアを持っている可能性が高い。
- 意思決定のエネルギーはここぞという時に使うべき。自分をパターン化すること。些細な事に気をとられていたら仕事が終わらない。
WebCrawler in Scala
Crawler in Scala
検索Crawlerを作る - Web就活日記
以前はnutchを使ったcrawlerを試してみましたが、今回はcrawler自体をscalaで書いているものをまとめようと思います。インターネットで紹介されているものの中には全然使えないものもあったりするので、選択には気をつけてください。個人的にはまとめた結果からJoup、HtmlUnitDriverが記述や設定が簡単で手軽に実行できるという点でお薦めしたいツールになっています。nomad
denigma/nomad
JDK/JRE7、MongoDB、Debianを必要とします。これによって私はテストしませんでしたが。sourceの更新も2年前で止まってしまっていますね。。application.conf、filters.groovy、seeds.txtの3つのファイルを記述するだけで簡単に動かせて、結果をMongo上で確認できるようなのでDebianUserは試してみると良いかもしれません。simplewebcrawler
Srirangan/simplewebcrawler
ScalaとGridGainを必要とします。GridGainでjobを分割することで並列分散処理をやっているみたい。GridGainを使うというニッチ派は聞いた事が無いので、何かしら問題が発生した時に大変そうです。fureteur
gip/fureteur
おそらく紹介する中で一番作りこまれている印象を受けました。RabbitMQを利用した分散Crawlingを実現、akkaのactorでmoduleを実装、スケジューリングを管理してくれる、sbtで用意されているので簡単に利用できる等の特徴があります。本気でCrawlerを作りたい人向けですかね。crawler
bplawler/crawler
指定したURLから再帰的にリンクを見つけてCrawlしてくれます。ただしドキュメントがほとんど無く初心者には設定がよくわからない、READMEの記述が誤っている、複雑なTagのパースがうまくできない(?)、様々なサイトのリンクを取得しようとするとExceptionがたくさん出て処理が進まないなど使用するのは困難と言えます。評価出来る内容としてはsbtによるbuild管理と、少ないコード記述でcrawlerができてしまうこと。以下は簡単に導入の内容を記載しておきます。$ git clone https://github.com/bplawler/crawler.git $ cd crawler $ cp build.sbt build.sbt.bak $ vi build.sbt $ diff -u build.sbt build.sbt.bak --- build.sbt 2014-12-30 09:17:54.895512815 +0900 +++ build.sbt.bak 2014-12-30 09:48:18.683566361 +0900 @@ -8,8 +8,6 @@ scalacOptions := Seq("-deprecation", "-unchecked", "-feature") -mainClass in (Compile, run):= Some("crawl") - libraryDependencies ++= Seq ( "net.sourceforge.htmlunit" % "htmlunit" % "2.15" , "org.specs2" %% "specs2" % "1.14" % "test" $ vi src/main/scala/test.scala //下のファイルを記述 $ sbt > run [info] Compiling 1 Scala source to /home/yuta/work/git/crawler/target/scala-2.10/classes... [info] Running crawlimport crawler._ import java.io._ class TestCrawler extends Crawler { def crawl = { navigateTo("http://search.yahoo.co.jp/search?p=scala") { } onCurrentPage { try { forAll(div having xPath("//body/div")) { println(from(anchor having xPath("a")).getTextContent) } } catch { case e : NoSuchElementException => println("NO TAG") case e : org.apache.http.client.ClientProtocolException => println("PROTOCOL ERROR") } } } } object crawl { def main(args: Array[String]) { val c = new TestCrawler c.crawl } }JSoup
jsoup Java HTML Parser, with best of DOM, CSS, and jquery
CrawlerというよりはHTML Parserの紹介になってしまいますが、JavaのHTML ParserでJsoupという強力なLibraryがあります。これをScalaから実行します。XPathやDomでの操作が簡単にできるので気軽に試せます。$ git clone https://github.com/yutakikuchi/scala.gi $ cd scala/scala-jsoup $ cat build.sbt name := "scala-jsoup" scalaVersion := "2.10.2" scalaBinaryVersion := "2.10" mainClass in (Compile, run):= Some("crawl") libraryDependencies += "org.jsoup" % "jsoup" % "1.8.1" $ vi src/main/scala/crawl.scala //下のscalaを記述 $ sbt > runimport org.jsoup._ import collection.JavaConverters._ object crawl { def main(args: Array[String]) { try { val urlstr = "http://search.yahoo.co.jp/search?p=scala" val doc = Jsoup.connect(urlstr).get val tag = doc.select("#res > #web > ol > li").asScala.head println(tag) } catch { case e : NoSuchElementException => println("NO TAG") } } }HtmlUnitDriver
HtmlUnitDriver
HtmlUnitDriverはJavaで実装されています。Seleniumなどの自動テストツールにも組み込まれているWebDriverを使って自動的にリンクを横断してCrawlすることも可能でとても便利。また設定や記述も簡単で、Jsoupと同様にXPathやDom操作を得意としています。$ git clone https://github.com/yutakikuchi/scala.gi $ cd scala/scala-webdriver $ cat build.sbt name := "scala-webdriver" scalaVersion := "2.10.2" scalaBinaryVersion := "2.10" mainClass in (Compile, run):= Some("crawl") libraryDependencies += "org.seleniumhq.webdriver" % "webdriver-htmlunit" % "0.9.7376" $ vi src/main/scala/crawl.scala //下のscalaを記述 $ sbt > runimport scala.collection.JavaConversions._ import org.openqa.selenium.htmlunit.HtmlUnitDriver import org.openqa.selenium._ object crawl { def main(args: Array[String]) { try { val urlstr = "http://search.yahoo.co.jp/search?p=scala" val driver = new HtmlUnitDriver() driver.get(urlstr) var res = driver.findElementsByXPath("//li/a") for(r <- res) { println(r) } } catch { case e : NoSuchElementException => println("NO TAG") } } }
データサイエンティスト養成読本
データサイエンティスト養成読本 R活用編 【ビジネスデータ分析の現場で役立つ知識が満載! 】 (Software Design plus)
- 作者: 酒巻隆治,里洋平,市川太祐,福島真太朗,安部晃生,和田計也,久本空海,西薗良太
- 出版社/メーカー: 技術評論社
- 発売日: 2014/12/12
- メディア: 大型本
- この商品を含むブログ (1件) を見る
データサイエンス
参加したイベントで頂いた「データサイエンティスト養成読本 R活用編」の冒頭を中心に読んでみました。前に出ていた同等の養成本よりデータサイエンティスト養成の問題と本書の位置づけがはっきりしていて読みやすかったと思います。21世紀で最もセクシーな職業という噂のデータサイエンティストについて、本の中で重要だと思った内容の意訳をメモしておきます。(あまりデータサイエンティストの話はまとめてなく、結局は統計の話ばかりメモしてしまっています。すみません。)
- アカデミック領域と異なるビジネス領域でのノウハウの少なさ。
- データの前処理のような泥臭い話は書籍やWebには出てこない。
- 使用するツールに拘りを持たず、効率を重視して使い分けが必要。
- ビジネスへの意思決定にも携わるため、新卒からいきなり経験させてもらえない。
- 3つのキャリアタイプがあり、組織の上では各キャリアタイプの人材の協力が必要。
- ビジネス寄りのタイプ(ユーザーの気持ち理解)
- 統計理論に強いタイプ(統計解析)
- エンジニアリングに強いタイプ(データ処理のコードを書く)
- R言語はメモリに載り切らないデータ量、リアルタイム処理には不向き。
- 統計学の全体像
- 標準偏差は分散の平方根。
- 検定と一部のデータを利用してもデータ感に差があると言えるかを確認する手法。差がない確率が一定未満の場合は差があると判定。
- クラスタリングには階層的、非階層的がある。非階層的な例としてk-meansがある。
- アソシエーション分析を行って、有益なルールを探す。
- 決定木はツリー構造で可視化できるのでわかりやすい、CARTが有名。CARTはジニ係数でデータを分割。ジニ係数は0〜1の値、0だと平等、1だと不平等。
- 機械学習は目的変数が離散型か連続値かによってクラス分類、数値予測に分けられる。
- クラス分類ではデータ数が少ない時に精度が最も高いとされるSVMが有名。
- ランダムフォレストはアンサンブル学習(集団学習)により分類/回帰を行う。アンサンブル学習は弱学習機をたくさん作って結果の多数決を取る。ランダムフォレストは数値予測にも利用可能。
- 線形回帰は目的変数を直線的な関係で予測。評価指標にはRMSE(平均2乗誤差)、決定係数などを利用する。
以上が第一、二章の要約でした。個人的にもデータサイエンティストは何でも出来る人として見られる事が多く、データの分析からビジネスの未来を考えられる人と思われる事が多いように感じていますが、データサイエンティスト側としてはそれは要求レベルが高過ぎることで、決して一人ではできるような内容ではないと言えます。エンジニアにもネットワークエンジニア、Webアプリケーションエンジニア(サーバーサイドエンジニア、フロントエンジニア)、スマートフォンアプリエンジニアなど細かく定義できるようにデータサイエンティストにも当然得意不得意な領域があって、誰しもがコンサルタントやマーケッターなどの領域をカバーできないという世間一般の認識を少し変えていきたいですね。
What is spray?
What is spray
spray | Introduction » What is spray?
adtech × scala勉強会でsprayの事例が幾つかあったので軽く触ってみることにしました。上のWhat-is-sprayで紹介されている内容をここでも簡単に記述しておくと以下のような感じです。2014.12.23日現在、最新のVersionは1.3.2のようです。尚、本記事の実験環境はCentOS Linux release 7.0.1406 (Core)で動かしています。
- Akka上で動く、HTTP/RESTをサポートしたクライント/サーバーサイドのライブラリ。
- JVMのlayerを触りたい場合もレガシーなJavaの実装が必要なくscalaのAPIの記述で事が足りる。
- sprayはフレームワークというよりはライブラリ志向で作られている(哲学的に)。
- 完全非同期、Non-Blocking。
- ActorとFutureベース。
- ハイパフォーマンス、軽量、テスト可能。
- twirlというtemplate engineもある。
- 中心となるspray-io、インメモリのキャッシュであるspray-caching、spray-io上でClient/Serverとして動くspray-can、Servlet上で動かすためのspray-servletなどのモジュールが用意されている。
Getting Start
Hello world
Getting Started with sbt — Installing sbt on Linux
まずはscalaのbuild toolをinstall、次にsprayを動かしてみます。プロジェクト名はhello-worldとします。以下の実行によりHTMLを吐き出すところまでは出来ました。spray-templateはspray-can上でspray-routingを実行するためのtemplateなようです。$ wget https://dl.bintray.com/sbt/rpm/sbt-0.13.7.rpm $ sudo yum localinstall sbt-0.13.7.rpm -y $ git clone git://github.com/spray/spray-template.git hello-world $ cd hello-world $ sbt > test > re-start $ curl "http://127.0.0.1:8080/" <html> <body> <h1>Say hello to <i>spray-routing</i> on <i>spray-can</i>!</h1> </body> </html>Source Code
以下のパスに設定ファイルおよび起動、実行されているプログラムが記載されています。
hello-world/src/main/resources/application.conf hello-world/src/main/scala/com/example/Boot.scala hello-world/src/main/scala/com/example/MyServer.scala実行したいPathinfoを切り替えたい場合はMyServer.scalaの中のMyServiceに記載されているpathのパラメータを変更します。
// this trait defines our service behavior independently from the service actor trait MyService extends HttpService { val myRoute = path("hello") { get { respondWithMediaType(`text/html`) { // XML is marshalled to `text/xml` by default, so we simply override here complete { <html> <body> <h1>Say hello to <i>spray-routing</i> on <i>spray-can</i>!</h1> </body> </html> } } } } }re-start後に再度curlします。helloのパスが有効化されました。
$ curl "http://127.0.0.1:8080/" The requested resource could not be found. $ curl "http://127.0.0.1:8080/hello" <html> <body> <h1>Say hello to <i>spray-routing</i> on <i>spray-can</i>!</h1> </body> </html>以下のように記述するとpathの条件分岐が可能です。
val myRoute = path("hello") { get { respondWithMediaType(`text/html`) { // XML is marshalled to `text/xml` by default, so we simply override here complete { <html> <body> <h1>Say hello to <i>spray-routing</i> on <i>spray-can</i>!</h1> </body> </html> } } } } ~ path("world") { get { respondWithMediaType(`text/html`) { // XML is marshalled to `text/xml` by default, so we simply override here complete { <html> <body> <h1>Say world to <i>spray-routing</i> on <i>spray-can</i>!</h1> </body> </html> } } } }上記はvalおよびpathの定義で分岐させましたが、traitの分岐も可能です。こうするとCodeの可読性が良くなる気がします。
class MyServiceActor extends Actor with ServiceHello with ServiceWorld { def actorRefFactory = context def receive = runRoute(helloRoute ~ worldRoute) } trait ServiceHello extends HttpService { val helloRoute = path("hello") { get { respondWithMediaType(`text/html`) { // XML is marshalled to `text/xml` by default, so we simply override here complete { <html> <body> <h1>Say hello to <i>spray-routing</i> on <i>spray-can</i>!</h1> </body> </html> } } } } } trait ServiceWorld extends HttpService { val worldRoute = path("world") { get { respondWithMediaType(`text/html`) { // XML is marshalled to `text/xml` by default, so we simply override here complete { <html> <body> <h1>Say world to <i>spray-routing</i> on <i>spray-can</i>!</h1> </body> </html> } } } } }twirl
spray/twirl
TwirlというTemplateEngineを導入してみます。まずはtwirlをproject/plugin.sbt、build.sbtに以下の内容を追加します。各行の追加は空行が必要なので気をつけてください。# project/plugin.sbt addSbtPlugin("io.spray" % "sbt-revolver" % "0.7.2") resolvers += "spray repo" at "http://repo.spray.io" addSbtPlugin("io.spray" % "sbt-twirl" % "0.7.0") # build.sbt Twirl.settings次にtemplateを追加します。main直下にtwirlディレクトリを作成し、index.scala.htmlなどを配置します。index.scala.htmlの内容を以下のものとします。templateの記述方法はplayframeworkと同等になります。最後にMyServer.scalaで変数のpに値を渡してあげると、埋め込んで出力してくれます。
// MyServer.scala trait MyService extends HttpService { val myRoute = path("") { get { respondWithMediaType(`text/html`) { // XML is marshalled to `text/xml` by default, so we simply override here complete ( html.index.render("twirl").body ) } } } } // index.scala.html @(p: String) <!DOCTYPE html> <html> <body> <p>Hello-World @p</p> </body> </html>curl "http://127.0.0.1:8080/" <!DOCTYPE html> <html> <body> <p>Hello-World twirl</p> </body> </html>
MysqlとのConnection
Slick OR Mapper
sprayからmysqlに接続するためにSlickというOR Mapperを利用します。まずはmysqlとの接続driverとslickをbuild.sbtに加えます。Slickは使用するVersionによって記述が大きく異なるので注意が必要です。この記事では2.1.0を利用しています。Slick - Scala Language Integrated Connection Kit — Slick 2.1.0 documentation
$ diff -u build.sbt build.sbt.bak --- build.sbt 2014-12-23 13:45:33.476276736 +0900 +++ build.sbt.bak 2014-12-23 13:39:23.122278989 +0900 @@ -16,8 +16,6 @@ "com.typesafe.akka" %% "akka-actor" % akkaV, "com.typesafe.akka" %% "akka-testkit" % akkaV % "test", "org.specs2" %% "specs2-core" % "2.3.11" % "test", - "mysql" % "mysql-connector-java" % "5.1.25", - "com.typesafe.slick" %% "slick" % "2.1.0" ) }あまり良く無い例ですが、http://127.0.0.1:8080にアクセスしたタイミングでMyServiceから直接Mysqlに接続してTableの作成、データのInsertを行います。理想としてはDBSchemaClass、Connectionを別途定義することだと思うので、気になった人は自由に書き変えてください。
trait MyService extends HttpService { class Customers(tag: Tag) extends Table[(Int, String, String)](tag, "customers") { def id = column[Int]("id", O.PrimaryKey) def name = column[String]("name") def email = column[String]("email") def * = (id, name, email) } val customers = TableQuery[Customers] Database.forURL("jdbc:mysql://localhost/test?user=root&password=", driver = "com.mysql.jdbc.Driver") withSession { implicit session => try { (customers.ddl).drop } catch { case e:Exception => println("table not found") } (customers.ddl).create customers ++= Seq( (1, "foo", "foo@com"), (2, "bar", "bar@com") ) } val myRoute = path("") { get { respondWithMediaType(`text/html`) { // XML is marshalled to `text/xml` by default, so we simply override here complete { <html> <body> Create/InsertDB is Completed </body> </html> } } } } }$ curl "http://127.0.0.1:8080/" <html> <body> Create/InsertDB is Completed </body> </html> mysql> select * from customers; +----+------+---------+ | id | name | email | +----+------+---------+ | 1 | foo | foo@com | | 2 | bar | bar@com | +----+------+---------+ 2 rows in set (0.00 sec)