-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.json
1 lines (1 loc) · 139 KB
/
index.json
1
[{"content":" はじめに PortugalのLisbonにて2月の22-24日にかけて開かれたICPRAM 2023に参加してきました。私にとって初めての海外\u0026amp;初めての現地参加の学会であり、学会そして海外の文化的な違いにふれられた、とても良い経験になりました。帰りは1人でありドイツに1泊しつつ帰ってきたことで海外旅行の自信もつきました。\n日程 2023/02/20 日本 羽田出発 2023/02/21 ドイツ フランクフルト乗り継ぎ \u0026amp; ポルトガル リスボン到着 \u0026amp; 観光 2023/02/22-24 ICPRAM 2023 2023/02/25 観光 \u0026amp; ポルトガル リスボン出発 \u0026amp; ドイツ ミュンヘン到着 2023/02/26 観光 \u0026amp; ドイツ ミュンヘン出発 2023/02/27 日本 羽田到着 学会 コロナの影響で現地開催されている学会が少なかったため、初めての現地参加の学会であり、生の学会の雰囲気を知ることができました。ICPRAM自体がパターン認識の学会であり、画像系の研究が多い学会で音声系の発表は少なかったです。\n発表を聴講するほうでも発表をすることも英語がつらかったです😵。特に興味を持って聞いてくれる人に英語のせいでうまく理解してもらえないことには歯がゆさを覚えました。一方で日本にいるとあまり感じない英語を使えないことのハンデを知ることができました。\n先生と発表したポスター↑\n発表のほかにもデータセット集めに協力したり、英語で雑談に話しかけられたりと普段経験しないことを経験することができました。\n観光 飛行機の関係で時間ができたため、観光もしました。\nLisborn,Portugal ポルトガル全体にほのぼのとした空気が流れていました。リスボン自体は比較的治安の良い町ですが落書きが至るところにあったり、乞食の方があちらこちらにいたりといった点には衝撃を受けました。全体としてやさしい雰囲気の住みやすそうな町でした。\n景色 Mosteiro dos Jerónimos(ジェロニモス修道院) Torre de Belém(ベレンの塔) Castelo de São Jorge(サンジョルジェ城) Santa Maria Maior de Lisboa(リスボン大聖堂) Munich,Germany 時間がなく2時間くらい歩き回っただけです。朝早かったのと日曜日だったことからお店が全然やっていなかったのが残念でした。\nMarienplatz (マリエン広場) 航空機 窓から見た景色がきれいでした。窓側の席が好きになりました。 星がきれい ","permalink":"https://hattomo.github.io/posts/main/23/q2/0519-icpram2023_portugal/jp/","summary":"\u003c!--\n\n--\u003e\n\u003ch2 id=\"はじめに\"\u003eはじめに\u003c/h2\u003e\n\u003cp\u003ePortugalのLisbonにて2月の22-24日にかけて開かれた\u003ca href=\"https://icpram.scitevents.org/?y=2023\"\u003eICPRAM 2023\u003c/a\u003eに参加してきました。私にとって初めての海外\u0026amp;初めての現地参加の学会であり、学会そして海外の文化的な違いにふれられた、とても良い経験になりました。帰りは1人でありドイツに1泊しつつ帰ってきたことで海外旅行の自信もつきました。\u003c/p\u003e","title":"ICPRAM 2023に参加してきました"},{"content":"About this websites This websites is a place to share and memorize what I have learned, experienced, and played in my life.\nDesign I focus on things below to build this websites.\n😀Modern We will try to experiment with new technologies as much as possible on this page. For example, we currently use Avif as our image format.\n🦚Beautiful This websites has simple Beautiful light/dark mode UI. Enjoy!\n⚡Fast I belive fast loading is so important. I had thought about the data you needed and optimized the data you receive in my sites. You can read the websites comfortably.\n📱Mobile frindly You also get great experience on your mobile.\n🔒Privacy and Security Both privacy and security are incredible important. I value it. To learn more, please visit privacy policy.\nSupported Platform This blog is supporting latest version of Chrome, Microsoft Edge, Safari, Firefox. Please use those browsers. There are some limitations in viewing images due to the use of Avif\nIf you use macOS Safari, you must use macOS Big Sur or later. If you use iOS, you must use iOS 16 or later. If you use Edge, you must use Windows 11 22h2 Moment 3 or Later License Source code in this blog is licensed under MIT. Other content is under CC-BY 4.0.\n","permalink":"https://hattomo.github.io/general/about_site/","summary":"\u003ch2 id=\"about-this-websites\"\u003eAbout this websites\u003c/h2\u003e\n\u003cp\u003eThis websites is a place to share and memorize what I have learned, experienced, and played in my life.\u003c/p\u003e\n\u003ch3 id=\"design\"\u003eDesign\u003c/h3\u003e\n\u003cp\u003eI focus on things below to build this websites.\u003c/p\u003e\n\u003ch4 id=\"modern\"\u003e😀Modern\u003c/h4\u003e\n\u003cp\u003eWe will try to experiment with new technologies as much as possible on this page. For example, we currently use \u003ccode\u003eAvif\u003c/code\u003e as our image format.\u003c/p\u003e\n\u003ch4 id=\"beautiful\"\u003e🦚Beautiful\u003c/h4\u003e\n\u003cp\u003eThis websites has simple Beautiful light/dark mode UI. Enjoy!\u003c/p\u003e\n\u003ch4 id=\"fast\"\u003e⚡Fast\u003c/h4\u003e\n\u003cp\u003eI belive fast loading is so important. I had thought about the data you needed and optimized the data you receive in my sites. You can read the websites comfortably.\u003c/p\u003e","title":"About this websites"},{"content":"はじめに Rakutenの業績は2022年1月から9月で2500億以上の赤字が発生している。EC(楽天市場)やフィンテック事業は2桁台成長を続けている一方、モバイル事業の赤字が四半期で1000億円ほど発生しておりこれが業績の重しとなっている。Rakutenは2023年中の単月黒字化を目指すと公言しており、今回は素人が決算資料を見てその可能性について考えてみる。なお、単純に黒字の可能性のみを考え、資金繰りについては考えない。また、決算資料は4半期ごとであるので四半期分の収益で考える。\nRakuten モバイル事業の決算 2022Q3 2022Q3の決算について現状を確認する。2022Q2では1243億万円の赤字であったが、逓減し、1209億円の赤字となっている。 モバイルセグメントの売り上げは、MNO、MVNOとして通信を提供するRakuten Mobile事業、世界のキャリアにRakuten Mobileで培ったソフトウェアやハードウェアを販売していくRakuten Synphony事業、電気やガスなどのRakuten engrgyの3つからなっている。決算の結果をしての表に示す。\nこれによると2022Q2から2022Q3の売り上げ増加の要因はエネルギー事業であり、世界的な電気料金の価格高騰により売り上げが増加しているのではないかと考える。しかし、モバイルセグメントの黒字化を考えるうえで大切なのはモバイル事業とシンフォニー事業をいかに売り上げを伸ばしていくかであると感じる。\n2023年中の黒字化 先ほどモバイルセグメントの黒字化を考えるうえで大切なのはモバイル事業とシンフォニー事業をいかに売り上げを伸ばしていくことであると推測した。ここではモバイル事業とシンフォニー事業のそれぞれについて詳しく考える。\nシンフォニー シンフォニー事業の売上高が今回初めて説明された。22Q4の売り上げは良くて2000億円ほどではないかと予想される。決算発表の中で利益率が示されていないがシンフォニー事業も投資段階であるため、赤字である可能性がある。仮に楽観的に10%の営業利益を出したとしても200億円にしかならず、1200億円の赤字を埋めるためには到底足りない。5年後に大きな収益を生んでいる可能性はあるが、2023年中の黒字化にはあまり貢献しないだろう。 モバイル よってモバイル事業の実力で黒字化する必要がありそうである。収支を改善する方法は3つに分けられる。\nARPUを上げる 新規ユーザーを獲得する ネットワークのコストを削減する それぞれについて考える\nARPUを上げる 現在のARPUが今回の決算で初めて公開された。エコシステムアップリフトをのぞけば、2000円ほどであると考えられる。現在、0円を廃止したことにより使い放題を標榜した新規加入者が増えていくことを考慮し、新規ユーザーのARPUが3000円としても2023Q4の全体のARPUは2300円を大幅に上回ることはないだろう。 新規ユーザーを獲得する これは大切なことである。現在455万人のMNOユーザーがいる。0円を行っていたときでさえ年間150万人ほどの新規ユーザーであったそのため、楽観的に2023Q4でも600万回線ほどでないかと考える。\nこの2つから2023Q4の収入を計算してみる。600万×2300円*3か月=414億円であり、22Q3と比べて210億円程度増収である。これでもまだまだ足りない。\nネットワークのコストを削減する 正直黒字化するためにはこれをどこまで行えるかにかかっているような気がする。 この図がどこまで正確なのかよくわかりませんが4G基地局数60000超、4G人口カバー率99%+を達成するのは2023年中とされており、4割ほどコストをが下がっているように感じられる。\nこれがPLのネットワーク費用に該当するのだとすれば2022Q3のネットワーク整備費用1030億円のうち、約400億円の削減が見込まれる。また、原価の項目も急速に低減しており100億円単位の改善が可能なのかもしれない。さらに販売管理費で100億円ほど減らせば、収支は改善する。\n黒字化できるか ここまで見てきたようにシンフォニー事業200億,ARPUの改善と新規ユーザーの獲得により210億,ネットワークコストの削減400億,原価の削減100億円、販売管理費の削減100億、で合計1010億円の改善を予想する。楽観的な見積もりを行ったつもりであるがこれでも黒字化には200億円ほど足りていない。ARPUにエコシステムアップリフトを600円ほど考慮しても、600円×600万回線×3か月=108億円であり、2023中の単月黒字化はかなり厳しいと感じる。 さらなるネットワーク整備費用の減少と新規ユーザーの獲得、ARPUが必要である。個人的にはモバイル事業に投資を続けていけばいつかは黒字にすることが可能であると考えるがその投資資金の捻出方法とどのように黒字にする計画なのかがあまり明確ではないことが、皆を不安にさせているように思う。2023年中の単月黒字化の目標は変わりないとするなら、根拠を示してほしいが、2023年Q4の決算を待って予想と見比べることを楽しみにしたいと思う。\nReference 楽天モバイルの2022Q3決算資料 ","permalink":"https://hattomo.github.io/posts/main/22/q4/1111-rakuten/jp/","summary":"\u003ch2 id=\"はじめに\"\u003eはじめに\u003c/h2\u003e\n\u003cp\u003eRakutenの業績は2022年1月から9月で2500億以上の赤字が発生している。EC(楽天市場)やフィンテック事業は2桁台成長を続けている一方、モバイル事業の赤字が四半期で1000億円ほど発生しておりこれが業績の重しとなっている。Rakutenは2023年中の単月黒字化を目指すと公言しており、今回は素人が決算資料を見てその可能性について考えてみる。なお、単純に黒字の可能性のみを考え、資金繰りについては考えない。また、決算資料は4半期ごとであるので四半期分の収益で考える。\u003c/p\u003e","title":"Rakuten Moblieの2022Q3決算を見て黒字化を考える"},{"content":"はじめに 音声認識技術は、スマートフォンの音声アシスタントや翻訳アプリなどを通して広く利用されている。近年の音声認識では深層学習を用いたConformerやContextNetなどのモデルが精度を大きく改善している。しかし、音声認識の精度は音声認識を行うモデルに言語モデルを統合することで改善することができることが知られている。言語モデルとは、ある時刻\\(t\\)の情報をもとに時刻\\(t+1\\)の出力を予測するモデルである。今回は、音声認識モデルの言語モデルの統合方法について説明する。\n言語モデルの統合方法 音声認識において、言語モデルの統合方法はshallow fusion, deep fusion, cold fusion, component fusionの4つがある。それぞれの統合方法の特徴を以下に示す。COMPONENT FUSION: LEARNING REPLACEABLE LANGUAGE MODEL COMPONENT FOR END-TO-END SPEECH RECOGNITION SYSTEMに違いがわかりやすく掲載されていたので図はそこから引用する。\nまず、言語モデルがない音声認識モデルの図を示す。 ここに言語モデルを足していくことになる。\nShallow fusion shallow fusionは最も多くのモデルに利用されているメジャーな方法である。 モデルの図を示す。 音声認識モデル部分には手を加えることなく、言語モデルを統合している。それぞれの出力を行った後言語モデルからの出力を\\(\\beta\\)倍して足している。 式は以下のようになる。\n\\[y_{t} = arg max(\\log{(y_{t}^{LAS})} + \\beta log{(y_{t}^{LM})})\\]\nDeep fusion Deep fustionは、言語モデルを内部の特徴量の段階で統合したモデルである。\n\\[ \\begin{aligned} g_t \u0026amp;= sigmoid(U_gs_t^{LM}+b) \\\\ \\hat{h}_{t}^{att} \u0026amp;= [h_t^{att};g_ts_t^{LM}] \\\\ y_t \u0026amp;= softmax(W_o^\u0026rsquo;\\hat{h}_t^{att}) \\end{aligned} \\]\nここで,\\([x;y]\\)は\\(x\\)と\\(y\\)をconcatしたものを表している。 \\(g_t\\)は\\(U_g\\)によって調整されるパラメータであり、言語モデルの出力\\(s_t^{LM}\\)の情報をそれぞれのパラメータについてどれだけ利用するのかを決めている。その後、ASRモデルからの出力とconcatして\\(y_t\\)を計算している。\nCold fusion \\[ \\begin{aligned} h_t^{LM} \u0026amp;= DNN(l_t^{LM}) \\\\ g_t \u0026amp;= sigmoid(U_g[h_t^{LM};h_t^{att}]+b) \\\\ \\hat{h}_t^{att} \u0026amp;= [h_t^{att};g_th_t^{LM}] \\\\ y_t \u0026amp;= softmax(W_o^\u0026rsquo;\\hat{h}_t^{att}) \\end{aligned} \\]\ncold fusion では、言語モデルを利用する際、言語モデルの特徴量からのみで\\(g_t\\)を計算していたが、deep fusionではASRモデルからの情報も利用して利用する言語モデルの特徴量を決定する。\ncomponent fusion cold fustionをベースに言語モデルを切り離した方法である。まず初めに、言語モデルをASRモデルのラベルによって学習する。これによって高速で学習し、学習データのドメインにも対応できる。また、言語モデル自体を取り替えることもできる。また、このモデルでは言語モデルの特徴量を早期に結合しているため、よりASRモデルの浅い段階から学習に影響を与えるよう改良されている。\nReference Towards better decoding and language model integration in sequence to sequence models Cold Fusion: Training Seq2Seq Models Together with Language Models On using monolingual corpora in neural machine translation COMPONENT FUSION: LEARNING REPLACEABLE LANGUAGE MODEL COMPONENT FOR END-TO-END SPEECH RECOGNITION SYSTEM ","permalink":"https://hattomo.github.io/posts/main/22/q4/1102-asr-lm/jp/","summary":"\u003ch2 id=\"はじめに\"\u003eはじめに\u003c/h2\u003e\n\u003cp\u003e音声認識技術は、スマートフォンの音声アシスタントや翻訳アプリなどを通して広く利用されている。近年の音声認識では深層学習を用いたConformerやContextNetなどのモデルが精度を大きく改善している。しかし、音声認識の精度は音声認識を行うモデルに言語モデルを統合することで改善することができることが知られている。言語モデルとは、ある時刻\\(t\\)の情報をもとに時刻\\(t+1\\)の出力を予測するモデルである。今回は、音声認識モデルの言語モデルの統合方法について説明する。\u003c/p\u003e","title":"ASRの言語モデルの統合方法"},{"content":"HuBERT 発表学会と発表者 Facebook AI IEEE/ACM Transactions on Audio, Speech, and Language Processing 29 (2021) https://arxiv.org/pdf/2106.07447.pdf\nHuBERT誕生の背景 深層学習を用いた音声認識では、Transformerの登場によって従来のLSTMなどのRNNベースの手法から精度がさらに向上した。しかし、この手法はラベル付きの学習データが大量に必要となる。これを回避するためにSelf-Supervised learningを活用しようという動きが見られるようになった。HuBERTは音声におけるBERTのような役割を果たすと考えている。\nHuBERTの概要 Hidden-UnitBERT(HuBERT)は、自己教師あり学習(Self-supervised learning)による音声の表現学習モデルである。自己教師あり学習とは、ラベルのついていないデータに対して、データからラベルを自動的に作成できるような汎用的なタスクによる学習を指す。また、自己教師あり学習のような、特定のタスクに依存せず汎用的な特徴表現を獲得する学習を総称して表現学習と呼ぶ。HuBERTは、クラスタリングにより音声データから疑似ラベルを作成し、音声データの一部をマスクしてその疑似ラベルを予測するMasked Language Model(MLM)による事前学習を行う。なお、疑似ラベルは学習中に更新され、特徴表現が徐々に改善される。音声データに対するMLMの学習を行うことで、HuBERTは音響モデルと言語モデルの両方を学習する。その後、音声認識などの目的のタスクによるFine-Tuningを行うことで、少量のラベル付きデータであっても高い性能を有することができる。\nHuBERTのモデル構造 HuBERTのモデル構造はWav2vec2.0を基にしている。HuBERTのモデル構造を表に示す。HuBERTは、CNNEncoder、TransformerEncoder、ProjectionLayerから構成される。HuBERTは、MFCCなどの特徴量抽出を事前に行わず、代わりに一次元畳み込みからなるCNNEncoderによって生の音声から特徴量抽出を行う。その後、Transformerによってエンコードされ、全結合層からなるProjectionLayerに渡される。ProjectionLayerは、事前学習において疑似ラベルを予測するために用いられる層である。また、HuBERTはモデルの規模によってBASE、LARGE、X-LARGEの3つの構成が提案されている クラスタリングによる疑似ラベルの作成 HuBERTは、DeepClusterより着想を得ており、k-meansなどのクラスタリングにより音声データから疑似ラベルを作成する。CNNEncoderによって特徴量抽出された音声特徴量\\(X=[\\bm{x}_1,\\cdots,\\bm{x}_T]\\)に対して、フレーム単位の疑似ラベル\\(Z=[z_1,\\cdots,z_T]\\)は、生の音声より特徴量抽出されたMFCCをクラスタリングすることで得られる。なお、\\(z_t\\in[C]\\)は\\(C\\)クラスのカテゴリ変数であり、クラス数はクラスタリングを行うときのクラスタ数によって決定される。また、系列長\\(T\\)は、MFCCのフレーム化に合わせて、CNNEncoderのカーネルサイズやストライドによって調整されている\n疑似ラベルの改良HuBERTは、後述するMasked Language Model(MLM)による学習中にクラスタリングを反復的に行うことで、疑似ラベルを改良する。学習前はMFCCに対してクラスタリングを行っていたが、学習中はモデル中間の出力に対して行う。クラスタリングと学習を交互に行うことで、モデルおよび疑似ラベルを段階的に改善する。ClusterEnsemblesHuBERTは、複数のクラスタリングによる疑似ラベルを用いたアンサンブル学習を行う。複数のクラスタリングは、異なるサイズのクラスタ数や異なる特徴量によるクラスタリングによって行われる。複数のクラスタリングを行うことで、異なるスケール(母音、子音、セノン)における疑似ラベルを作成することができる。以下、\\(k\\)個目のクラスタリングによる疑似ラベルを\\(Z^{(k)}=[z^{(k)}_1,\\cdots,z^{(k)}_T]:(z^{(k)}_t\\in C^{(k)})\\)とする。また、クラスタ\\(c\\)の重心ベクトルを\\(\\bm{e}_c\\)とする。\nMaskedLanguageModelによる事前学習 MLMは、系列データに対して一定の確率でマスクし、そのマスクされた内容を予測するタスクである。HuBERTのMLMでは、音声特徴量\\(\\bm{x_{1}},\\cdots,\\bm{x_{T}}\\)の各フレームに対して、確率\\(p\\)で開始インデックスとして選択し、長さが\\(l\\)のマスクをかける。以下、マスクされるフレームの集合を\\(M\\subset[T]\\)とし、\\(X\\)を\\(M\\)の範囲でマスクした系列を\\(\\tilde{X}=r(X,M)\\)とする。なお、実験では、\\(p=0.08、l=10\\)が用いられている。MLMの損失\\(Lm\\)は交差エントロピーに基づいており、マスク部分を予測するHuBERTモデルfに対して以下の式で表される。\\[L_m(f;X,{Z^{(k)}},M)=-\\sum_{t\\in M}\\sum_{k}\\log p^{(k)}_f(z^{(k)}_t|\\tilde{X},t)\\]\nなお、\\(p^{(k)}_f(c|\\tilde{X},t)\\)は、HuBERTモデル\\(f\\)に対して、マスクされた音声特徴系列\\(\\tilde{X}\\)を入力としたときの時刻\\(t\\)における\\(k\\)個目の疑似ラベルのクラス\\(c\\)の予測確率を表す。マスクされた音声から事前に割り当てられたクラスタを予測するため、周囲のマスクされていない入力から高度な特徴表現を学習する必要がある。したがって、HuBERTはMLMによって、音声からより良い特徴量を抽出する音響モデルと系列の文脈を理解する言語モデルの両方が学習される\n疑似ラベルの予測確率の算出疑似ラベルの予測確率\\(p^{(k)}_f(c|\\tilde{X},t)\\)の算出方法について述べる。マスクされた音声特徴系列\\(\\tilde{X}\\)に対してHuBERTのTransformerEncoderの出力を\\([\\bm{o}_1,\\cdots,\\bm{o}_T]\\)、\\(k\\)個目のクラスタリングに対するProjectionLayerを\\(A^{(k)}\\)とすると、疑似ラベルの予測確率\\(p^{(k)}_f(c|\\tilde{X},t)\\)は以下の式で表される。\n\\[p_{(c|\\tilde{X},t)}^{(k)} = \\frac{\\exp(\\text{sim}(A^{(k)}\\bm{o_t},\\bm{e_c}))/\\tau}{\\sum_{C^{(k)}}^{c^{\\prime=1}} \\exp(\\text{sim}(A^{(k)}\\bm{o}_t,\\bm{e_c^\\prime})/\\tau)} \\]\nここで、\\(\\text{sim}(\\cdot,\\cdot)\\)はコサイン類似度を表し、\\(\\tau=0.1\\)は温度パラメータである。式(4。2)にあるように、疑似ラベルの予測確率は、各クラスタの重心ベクトルとの類似度をsoftmax関数により正規化した値である。\n","permalink":"https://hattomo.github.io/posts/main/22/q3/0715-hubert/jp/","summary":"\u003ch2 id=\"hubert\"\u003eHuBERT\u003c/h2\u003e\n\u003ch3 id=\"発表学会と発表者\"\u003e発表学会と発表者\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003eFacebook AI\u003c/li\u003e\n\u003cli\u003eIEEE/ACM Transactions on Audio, Speech, and Language Processing 29 (2021)\u003c/li\u003e\n\u003c/ul\u003e\n\u003ca href=\"https://arxiv.org/pdf/2106.07447.pdf\"\u003e\n \u003cdiv class=\"bcard-wrapper\"\u003e\n \u003cspan class=\"bcard-header withgfav\"\u003e\n \u003cdiv class=\"bcard-favicon\" style=\"background-image: url(https://www.google.com/s2/favicons?domain=https://arxiv.org/pdf/2106.07447.pdf)\"\u003e\u003c/div\u003e\n \u003cdiv class=\"bcard-site\"\u003e\n \u003cp\u003e\u003c/p\u003e\n \u003c/div\u003e\n \u003cdiv class=\"bcard-url\"\u003e\n \u003cp\u003ehttps://arxiv.org/pdf/2106.07447.pdf\u003c/p\u003e\n \u003c/div\u003e\n \u003c/span\u003e\n \u003cspan class=\"bcard-main withogimg\"\u003e\n \u003cdiv class=\"bcard-title\"\u003e\n \u003cp\u003e\u003c/p\u003e\n \u003c/div\u003e\n \u003cdiv class=\"bcard-description\"\u003e\u003c/div\u003e\n \u003cdiv class=\"bcard-img\" style=\"background-image: url()\"\u003e\n \u003c/div\u003e\n \u003c/span\u003e\n \u003c/div\u003e\n\u003c/a\u003e\n\u003ch3 id=\"hubert誕生の背景\"\u003eHuBERT誕生の背景\u003c/h3\u003e\n\u003cp\u003e深層学習を用いた音声認識では、Transformerの登場によって従来のLSTMなどのRNNベースの手法から精度がさらに向上した。しかし、この手法はラベル付きの学習データが大量に必要となる。これを回避するためにSelf-Supervised learningを活用しようという動きが見られるようになった。HuBERTは音声におけるBERTのような役割を果たすと考えている。\u003c/p\u003e","title":"HuBERT"},{"content":"はじめに USB PD (Power Delivery)を利用して充電を行うことができるデバイスが増加してきており,スマホやPC充電のデファクトスタンダードになろうとしています.今回はDigiforce 20W AC 充電器を購入したのでReviewします. 購入リンクは下記です.\nhttps://www.amazon.co.jp/dp/B091FKMBBD\nAmazon.co.jp: DIGIFORCE for iPhone 13 Charger, 20 W, PD Charger, Type-C Ultra Small, Rapid Charging, USB-C Type C Charger, PSE Certified, PD \u0026 QC3.0 Compatible, Folding Type, AC Adapter (White) : Electronics\nAmazon.co.jp: DIGIFORCE for iPhone 13 Charger, 20 W, PD Charger, Type-C Ultra Small, Rapid Charging, USB-C Type C Charger, PSE Certified, PD \u0026 QC3.0 Compatible, Folding Type, AC Adapter (White) : Electronics 内容物 ACアダプター本体 USB Type-C to Type-C ケーブル 説明書 Spec Specは以下の様になってます.\n規格 PD (Power Delivery) 3.0 出力 20W 電圧・電流値 5V=3A, 9V=2.2A, 12V=1.67A サイズ 28 x 28 x 33 mm 重さ 35g ACアダプター外観 ケーブルを接続する面はPDと書かれているだけで至ってシンプルです.また,裏側はプラグをしまう事ができるようになっています. 丁寧なパッケージング 外箱はシンプルで,高級感がないですが,その中にさらに電源とUSB-Cケーブルの箱が入っています.そちらには比較的きれいな印刷がなされており,質感が良いです.さらにそれぞれの箱に持ち手がついていたり(下の写真),説明書がビニールに入っていたり,充電器本体はスポンジに包まれていたり,ケーブルを纏めて置くための,マジックテープケーブルタイが付いていたりと細かいところに配慮があり心地よく感じました.(過剰包装気味ではある) 所感 スペック的にも,質感的にも良い買って満足の商品でした.このサイズであれば持ち運びも楽ですし軽いです.価格も1600円ほど(クーポン利用)であったので良い買い物であったと思います.\n","permalink":"https://hattomo.github.io/posts/main/22/q1/0114-digiforce-20w-ac-chager-review/jp/","summary":"\u003ch2 id=\"はじめに\"\u003eはじめに\u003c/h2\u003e\n\u003cp\u003eUSB PD (Power Delivery)を利用して充電を行うことができるデバイスが増加してきており,スマホやPC充電のデファクトスタンダードになろうとしています.今回はDigiforce 20W AC 充電器を購入したのでReviewします.\n購入リンクは下記です.\u003c/p\u003e","title":"Digiforce 20W AC Chager Review"},{"content":"ブログを書いているとリンクを示す際にブログカードが欲しくなる時があります。今回はブログカードをどのように作成するかをまとめます。\n作成方法の検討 ブログカードを作成する方法を調べたところ大きく分けて4つあるようでした。\nはてなブログのブログカードを使う ほかのプログカードを生成する外部のAPIを利用する localhostにブログカードのサーバーを立てる ブログカードを作成するためのHTML\u0026amp;CSSを自前で作成する それぞれを比較します\nはてなブログのブログカードを使う はてなブログのブログカードを使うと以下のように表示されます。\n1\u0026lt;iframe class=\u0026#34;hatenablogcard\u0026#34; style=\u0026#34;width:100%;height:155px;max-width:680px;\u0026#34; title=\u0026#34;\u0026#34; src=\u0026#34;https://hatenablog-parts.com/embed?url=http://apple.com\u0026#34; width=\u0026#34;300\u0026#34; height=\u0026#34;150\u0026#34; frameborder=\u0026#34;0\u0026#34; scrolling=\u0026#34;no\u0026#34;\u0026gt;\u0026lt;/iframe\u0026gt; この方法は簡単にブログカードを作ることができる反面、はてなさんのAPIがいつ終了してしまうかわからないこと(そもそも公式に使ってよいと書かれているわけはない)や\u0026lt;iframe\u0026gt;を利用して強引?に埋め込んでいる点やデザインを自由に変更できないという欠点があります。また、dark modeにも対応していません。\n他のプログカードを生成する外部のAPIを利用する iframely.comなどの外部のAPIを利用することでブログカードを作成することができます。このような方法では、無料枠の制限などを考慮する必要があります。 また、自前でAPIを作成することもできますがHugoの静的なサイトの理念からは離れてしまいます。\nhttps://enjoyall.comichi.com/aws_lambda/\n【初めてのaws】aws lambdaでウェブサイトのmetaタグ取得APIを作る | enjoyall\n前回、Amazon Web Service(AWS)に登録したので、今回は早速サービスを作ってみたいと思います。 awsへの登録がまだの場合は前回の記 localhostにブログカードのサーバーを立てる 面白い方法ではありますが上と同様にHugoの静的なサイトの理念からは離れてしまいます。 例:\nSIS Lab\nhttps://www.meganii.com/blog/2020/02/02/blogcard-in-hugo/\nHugoでAMP対応のブログカードを作る - SIS Lab\n「Hugoでもブログカードを利用したい」そう考えているところに以下の記事がTwitterのTLで流れてきたので、試してみました。Hugoでブログカードに対応する | Hugo 入門 / 解説 | nasust dev blog ブログカードを作成するためのHTML\u0026amp;CSSを自前で作成する テンプレートを作るまでが大変ですが、デザインも自由にできるため、今回はこれを行うことにしました。方法としてはブックマークレットでOPGを利用してブログカードを作るために必要な情報を取得します。\nOPGを取得するブックマークレットを作成する javascriptで以下のようなコードを作成し、ブックマークレットに登録しました。ブックマークレットとはブラウザのブックマークにjavascriptを登録したもののことです。ブックマークレットに登録するためにコードをclouse compilerのSIMPLE_OPTIMIZATIONSで最適化しました。faviconを取得するためにGoogleのAPIを利用しています。また、サイトがhttpプロトコルの場合はnavigator.clipboardapiが利用できないため、プロンプトから手動でコピーします。\n1javascript: ( 2 function () { 3 console.log(\u0026#34;v1.0.2\u0026#34;); 4 var url = location.href; 5 6 // get title 7 try { 8 title = document.querySelector(\u0026#39;meta[property=\u0026#34;og:title\u0026#34;]\u0026#39;).getAttribute(\u0026#39;content\u0026#39;); 9 } catch (e) { 10 title = document.title; 11 } 12 13 // get og image 14 try { 15 ogimage = document.querySelector(\u0026#39;meta[property=\u0026#34;og:image\u0026#34;]\u0026#39;).getAttribute(\u0026#39;content\u0026#39;); 16 } catch (e) { 17 ogimage = \u0026#39;\u0026#39;; 18 } 19 20 // site name 21 try { 22 sitename = document.querySelector(\u0026#39;meta[property=\u0026#34;og:site_name\u0026#34;]\u0026#39;).getAttribute(\u0026#39;content\u0026#39;); 23 } catch (e) { 24 sitename = \u0026#39;\u0026#39;; 25 } 26 27 // get decription 28 try { 29 description = document.querySelector(\u0026#39;meta[property=\u0026#34;og:description\u0026#34;]\u0026#39;).getAttribute(\u0026#39;content\u0026#39;); 30 } catch (e) { 31 try { 32 description = document.querySelector(\u0026#39;meta[name=\u0026#34;description\u0026#34;]\u0026#39;).getAttribute(\u0026#39;content\u0026#39;); 33 } catch (e) { 34 try { 35 description = document.querySelector(\u0026#39;meta[name=\u0026#34;Description\u0026#34;]\u0026#39;).getAttribute(\u0026#39;content\u0026#39;); 36 } catch (e) { 37 description = \u0026#39;\u0026#39;; 38 } 39 } 40 } finally { 41 description = description.replace(/\\r?\\n/g, \u0026#34;\u0026#34;); 42 } 43 44 // get icon 45 try { 46 favicon = document.querySelector(\u0026#39;link[rel=\u0026#34;icon\u0026#34;]\u0026#39;).getAttribute(\u0026#39;href\u0026#39;); 47 } catch (e) { 48 favicon = \u0026#39;\u0026#39;; 49 } 50 51 var source = `\u0026lt;a href=\u0026#34;${url}\u0026#34;\u0026gt; 52 \u0026lt;div class=\u0026#34;bcard-wrapper\u0026#34;\u0026gt; 53 \u0026lt;span class=\u0026#34;bcard-header withgfav\u0026#34;\u0026gt; 54 \u0026lt;div class=\u0026#34;bcard-favicon\u0026#34; style=\u0026#34;background-image: url(https://www.google.com/s2/favicons?domain=${url})\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; 55 \u0026lt;div class=\u0026#34;bcard-site\u0026#34;\u0026gt; 56 \u0026lt;p\u0026gt;${sitename}\u0026lt;/p\u0026gt; 57 \u0026lt;/div\u0026gt; 58 \u0026lt;div class=\u0026#34;bcard-url\u0026#34;\u0026gt; 59 \u0026lt;p\u0026gt;${url}\u0026lt;/p\u0026gt; 60 \u0026lt;/div\u0026gt; 61 \u0026lt;/span\u0026gt; 62 \u0026lt;span class=\u0026#34;bcard-main withogimg\u0026#34;\u0026gt; 63 \u0026lt;div class=\u0026#34;bcard-title\u0026#34;\u0026gt; 64 \u0026lt;p\u0026gt;${title}\u0026lt;/p\u0026gt; 65 \u0026lt;/div\u0026gt; 66 \u0026lt;div class=\u0026#34;bcard-description\u0026#34;\u0026gt;${description}\u0026lt;/div\u0026gt; 67 \u0026lt;div class=\u0026#34;bcard-img\u0026#34; style=\u0026#34;background-image: url(${ogimage})\u0026#34;\u0026gt; 68 \u0026lt;/div\u0026gt; 69 \u0026lt;/span\u0026gt; 70 \u0026lt;/div\u0026gt; 71\u0026lt;/a\u0026gt;`; 72 prompt(\u0026#39;Copy to clipboard is not supported for this page. Please copy manually\u0026#39;, source); 73 // console.log(title); 74 // console.log(ogimage); 75 // console.log(description); 76 // console.log(favicon); 77 } 78)(); HTML,CSSは下のサイトを参考にカスタマイズしました。変更点としてはOPG画像とブログカードを角丸にしたことと、タイトルやサイト名の上だけでなくカード全体をリンクとしたこと、ダークモードに対応したこと、最大の横幅を長くしたことです。\nCottpic\nhttp://io.cottpic.com/simple-blogcard-generator.html\nリッチリンクを手軽に作成! ブログカードジェネレーター\n画像以外は外部サービスに依存しないタイプのブログカードを手軽に作成できるツールです。入力したURLのページのOGPタグから、サイト名、ディスクリプション、OGP画像のURL等を取得できます。 1.bcard-wrapper{ 2 display: block; 3 width: 100%; 4 max-width: 800px; 5 margin: 10px 0px; 6 padding: 12px; 7 border: 1px solid #e0e0e0; 8 border-radius: 5px; 9} 10 11.dark .bcard-wrapper{ 12 border-color: #333; 13} 14.bcard-site,.bcard-url{ 15 font-size: 12px; 16 line-height: 1.3; 17 overflow: hidden; 18 max-height: 15px; 19 display: -webkit-box; 20 -webkit-box-orient: vertical; 21 -webkit-line-clamp: 1; 22} 23.bcard-header { 24 position: relative; 25 height: 30px; 26 margin-bottom: 5px; 27 display: block; 28} 29.withgfav{padding-left: 23px;} 30.bcard-favicon {position: absolute; 31 top: 0px; left:0px; width:16px; height:16px;} 32.bcard-main{ 33 overflow: hidden; 34 position: relative; 35 display: block; 36} 37.withogimg{ 38 padding-right: 110px; 39 height: 100px; 40} 41 .bcard-img { 42width: 100px; 43height: 100px; 44position: absolute; 45 top: 0; 46 right: 0; 47 background-size:cover; 48 background-position:center center; 49 border-radius: 5px; 50} 51 .bcard-title{ 52 font-size: 17px; 53 margin: 0 0 2px; 54 line-height: 1.4; 55 max-height: 47px; 56 overflow: hidden; 57 display: -webkit-box; 58 -webkit-box-orient: vertical; 59 -webkit-line-clamp: 2; 60 font-weight: bold; 61} 62 .bcard-description { 63 line-height: 1.5; 64 font-size: 12px; 65 max-height: 72px; 66 overflow: hidden; 67 display: -webkit-box; 68 -webkit-box-orient: vertical; 69 -webkit-line-clamp: 3; 70} 71/* .bcard-title p{color:#424242;} */ 72.bcard-url p{color:#9e9e9e;} 73.bcard-title p:hover,.bcard-url p:hover,.bcard-site p:hover{text-decoration:underline;} これでブックマークレットをクリックするだけでブログカードのHTMLがコピーされます。実際の見た目はこのページのブログカードを見てください。\nYoutube Reference 上に出てきていないサイトで参考になったのは以下のサイトです。\nhttps://vanillaice000.blog.fc2.com/blog-entry-1074.html\nブログカード作成ブックマークレットをアップデートしました\nFC2ブログのみならず汎くお使い頂いているようで甲斐があったなぁ、と思っております ブログカード なんですが、アップデート、というか少しhtml内容を変更しました。... ","permalink":"https://hattomo.github.io/posts/main/21/q4/1031-making-blog-card/jp/","summary":"\u003cp\u003eブログを書いているとリンクを示す際にブログカードが欲しくなる時があります。今回はブログカードをどのように作成するかをまとめます。\u003c/p\u003e\n\u003ch2 id=\"作成方法の検討\"\u003e作成方法の検討\u003c/h2\u003e\n\u003cp\u003eブログカードを作成する方法を調べたところ大きく分けて4つあるようでした。\u003c/p\u003e","title":"ブログカードを作成する方法"},{"content":"はじめに ssdが余っていたのでssdケースを買って、外付けSSDとして利用できるようにしました。買ったssdケースはvigooleのHE-C326です。amazonで安くなっていたためで¥2,124でした。\n内容物 SSDケース本体 USB-C to USB-C ケーブル USB-C to USB-A ケーブル 放熱シート*2 SSDをセットする SSDはねじで固定し、放熱シートを貼った後、スライド式のケースに収納します。非常に簡単です。コントローラーチップにはJMS583が利用されているようです。\n↑ SSDケース本体\n↑ SSDを装着したところ\n所感 ケース全体としては、アルミ製であり、高級感があると共に放熱を行いやすい素材であると思います。工具なしでできるところも気軽でよいです。また、コントローラーJMS583は、有名メーカーのSSDにも利用されていたため品質に問題はないと感じました。実際Ubuntu,macOS,Windowsで問題なく利用できることを確認しました。色はMacBookのスペースグレイに近いです。1点気になった点として、放熱シートを貼った後、スライドして入れるのですが、かなりサイズがきつく(それ自体は放熱のため仕方ない気もするのですが)、再度取り出した際放熱シートが削れ、消しかすのようなものが発生していました。頻繁に取り出すのは向かないようです。\n","permalink":"https://hattomo.github.io/posts/main/21/q4/1016-vigoole-ssd-enclosure-review/","summary":"\u003ch2 id=\"はじめに\"\u003eはじめに\u003c/h2\u003e\n\u003cp\u003essdが余っていたのでssdケースを買って、外付けSSDとして利用できるようにしました。買ったssdケースは\u003ccode\u003evigoole\u003c/code\u003eの\u003ccode\u003eHE-C326\u003c/code\u003eです。amazonで安くなっていたためで¥2,124でした。\u003c/p\u003e","title":"Vigoole SSD Enclosure Review"},{"content":"はじめに 機会学習を行う際には、それがテキストであっても画像であっても音声であっても前処理を行うことが欠かせません。今回は音声の前処理についてまとめました。\n音声の下処理 1. フレームとフレームサイズ まずは音声波形を短い区間に分けていく。この取り出された時間のことをフレームと呼び、フレームの長さ(時間のことを)フレーム長やフレームサイズ(以下、フレームサイズとする)、次のフレームまでの間隔のことをフレームシフト、フレーム間隔(同、フレームシフト)と呼びます。音声認識ではフレームサイズは25msほどにすることが多い。音素を捉えるにはこのくらいのフレームサイズが良いためである。フレームサイズを小さくするほど、フーリエ変換の処理の高速が行うことができる。また、フレームシフトを設けるのは、フレームの間の繋がりの情報を保つためである。\n2. 窓掛け このように、フレームごとに分割した音声データに対してフーリエ変換を行なっていくわけですが、フーリエ変換は1周期分のデータに対して計算する理論であるため、そのまま計算を行うとフレームに含まれる信号が1周期でない分、誤差が生じる。しかし、フレームに含まれる音声データが1周期分になるよう分割することは困難である。そこで、窓関数というものを信号にかけることでこの問題を緩和する。窓関数にはいくつかの種類がありますが、代表的なハミング窓は以下の式で表される。\n\\[ f(x) = 0.54-0.46cos\\left(\\dfrac{2\\pi n}{N}\\right) (0≤n\u0026lt;N) \\]\nこれを掛け合わせると次のようになり、不連続部分が目立たなくなる。\n3. スペクトログラム 窓掛けを行った後、フーリエ変換したものをスペクトルという。複数フレームのスペクトルを重ね1つの図にしたものをスペクトログラムという。\nまた、フーリエ変換の絶対値を取ったある周波数成分の大きさを表すものを振幅スペクトル、振幅スペクトルの結果を二乗したある周波数のおける信号強度を表すのもをパワースペクトルという。\n4. プリエンファシス(高音強調) 周波数が高い信号ほど減衰しやすいため、減衰した部分を補うプリエンファシスが行われる。 以下のフィルタを信号に畳み込みを行うことで計算される。\n\\[ h(\\tau) = \\begin{cases} 1\u0026amp;(\\tau=0)\\\\ -\\alpha\u0026amp;(\\tau=1)\\\\ 0\u0026amp;(\\tau\u0026gt;1) \\end{cases} \\]\n\\(\\alpha\\)の値は、0.97ほどが利用される事が多い。\n5. ケプストラム、スペクトル微細構造、スペクトル包絡 ケプストラムは波形の対数振幅スペクトルの iFFT(逆フーリエ変換)として定義されものである。さらにこれの高周波成分のみをフーリエ変換したものをスペクトル微細構造、低次元成分のみをフーリエ変換したものをスペクトル包絡と呼ぶ。 スペクトル微細構造は、音声の内容を表しており、スペクトル包絡は、男性、女性などを識別する際に利用される。\n6. フィルタバンク 特定の範囲のみの周波数を通すバンドパスフィルタを複数利用し、次元を落としたものをフィルタバンクと呼ぶ。三角形の バンドパスフィルタの数をチャネル数と呼び,このチャネル数だけの次元が得られる。特定のポイントについてそのまま取り出さず、三角窓を用いる理由として、周辺のスペクトルの情報をある程度加味できるため、その周波数ビンが外れ値であってもそれを抑える働きがある。\n7. メル尺度とMFCC メル尺度は心理学者のStanley Smith Stevensらによって提案された、人間の音高知覚が考慮された尺度である。1000Hzの高さの感覚を1000メル(mel)と決めた上で、1000メルの半分の高さに感じた音を500メル、1000メルの2倍の高さに感じた音を2000メルという容量で定めている。一般に周波数fとメル尺度mの関係は次の式で表される。\n\\[m=m_{0}\\ln\\left(\\frac{f}{f_{0}}+1\\right)\\] \\[f=f_{0}\\left(\\exp{\\frac{m}{m_{0}}}-1\\right)\\] \\(f_{0}\\),\\(m_{0}\\)は、\\(f=m=1000\\)である時のパラメーターである。\n振幅スペクトルに対してm次元のメルフィルタバンク(メル尺度を考慮したいフィルタバンク)、離散フーリエ変換をかけた後、低周波数成分を取り出したものをMFCC(Mel-frequency cepstrum coeffient)と呼ぶ。離散フーリエ変換を行うことは、逆フーリエ変換と同様、周波数と時間の世界を変換している。\n","permalink":"https://hattomo.github.io/posts/main/21/q3/0912-preprocessing-audio-data/","summary":"\u003ch2 id=\"はじめに\"\u003eはじめに\u003c/h2\u003e\n\u003cp\u003e機会学習を行う際には、それがテキストであっても画像であっても音声であっても前処理を行うことが欠かせません。今回は音声の前処理についてまとめました。\u003c/p\u003e","title":"音声認識のための音声データの下処理"},{"content":"はじめに コンピューターにリモートアクセスを行いたいことがあります。この際の方法やTipsをまとめてみました。扱う方法はSSH,VNC,RDPです。\nSSHでリモート接続を行う SSHで外部からリモート接続を行う場合、セキュリティを考えると、多段階認証を行ったほうが良いです。多段階認証のサーバーは存在するとして、設定方法を記します。コンピュータの名前は以下のようにします。\n踏み台サーバー ターゲット(SSHでアクセスされるコンピュータ) クライアント(SSHでアクセスするコンピュータ) 下準備としてすべてのコンピュータにSSHをインストールし、ipアドレスの固定を行ってあるものとします。 またクライアント以外のOSはUbuntu 20.04を前提としますがほかのOSでもほぼ同様に可能です。\n鍵を生成する SSHでは、パスワードによる認証もできますが、セキュリティを鑑みると公開鍵認証による認証を利用するべきです。そこで公開鍵認証を行うための鍵を生成していきます。RSA,ed25519が現在よく利用されています。ed25519は、楕円曲線を利用した暗号でとりあえずこれを利用しておけば問題ありませんが、レガシーな環境ではed25519に対応していないOpen sshを利用していることがあり、そのような際にはRSAで鍵を作成します。RSAで鍵を生成する際には鍵長を4096以上に指定します。\n1# ed25519 2$ ssh-keygen -t ed25519 -C \u0026#34;\u0026#34; -f ~/.ssh/id_ed25519 3 4# RSA (legacy) 5$ ssh-keygen -t rsa -b 4096 -C \u0026#34;\u0026#34; -f ~/.ssh/id_rsa -Cオプションはコメントです。これをつけないと自動的に鍵を作成したコンピュータのusername@hostnameとコメントが入ってしまいます。ユーザー名とホスト名が入ってしまっても問題はないケースがほとんどだと思いますが、個人的に気になるのでコメントを消しています。GitHubの公式ドキュメントでは、メールアドレスをコメントにしています。-fオプションは、鍵の生成されるパスです。\nコマンドを実行するとパスワードを聞かれますが、そのままEnterキーを押します。 コマンドが完了したら、指定したパスに鍵があることを確認します。*.pubが公開鍵であり、拡張子がないものが秘密鍵です。秘密鍵は外部に漏れることのないように管理する必要があります。\n鍵を踏み台サーバーに配置する 生成された公開鍵を踏み台サーバーの~/.ssh/authorized_keysに追記します。ファイルがなければ新たに作成します。公開鍵の内容をただコピー&ペーストすればよいです。鍵を移動させる方法については、USBメモリなどで直接運ぶ方法、scpコマンドを利用する方法とssh-copy-idを利用する方法があります。可能であれば、ssh-copy-idを利用したほうが簡単です。\n1# scp 2scp [client pub key path] server_username@server_hostname:[path_on_host] 3# ssh-copy-id 4ssh-copy-id -i [client pub key path] server_username@server_hostname 鍵が、authorized_keysに書き込まれたことを確認しましょう。\n鍵生成から、鍵を配置するまでの一連の流れをターゲットに対しても行います。\nConfigに設定を追記する クライアントの~/.ssh/configファイルに設定を記述しておくことで短いコマンドでSSHを行うことができます。\n1ServerAliveInterval 120 2ServerAliveCountMax 3 3 4Host server 5 Hostname [ip addr or hostname] 6 User [server_username] 7 Port [port number] 8 IdentityFile [path to private ssh key for server] 9Host terget 10 Hostname [ip addr or hostname] 11 User [terget_username] 12 Port [port number] 13 IdentityFile [path to private ssh key for terget] 14 ProxyCommand ssh server -W %h:%p ServerAliveIntervalとServerAliveCountMaxは接続が切れないようにする設定です。sshでコマンドをしばらくたたかないと自動的に接続が切れてしまいます。それを防ぐためにsshd側が一定期間クライアントと通信していないときに、応答確認を行います。ServerAliveIntervalはその確認する感覚の秒数であり、ServerAliveCountMaxは試行回数です。\n最終的に以下のコマンドでターゲットへのsshが完了すれば成功です。\n1$ ssh terget ターゲットのsshdの設定 ServerAliveIntervalとServerAliveCountMaxのような設定をターゲットのsshd側ですることもできます。ターゲットの/etc/ssh/sshd_configを開き、\n1ClientAliveInterval 120 2ClientAliveCountMax 3 を追記します。設定を反映するには、\n1sudo service sshd restart でサービスを再起動します。\nVNCとRDP 晴れてSSHを行うことができるようになったわけですが、コマンドだけではなくディストップ環境が欲しい時もあります。そのようなときに活躍するものが、VNCとRDP(リモートデスクトッププロトコル)です。両方、リモートからデスクトップ環境を利用すために作られたものですが、実現する仕組みが異なっています。VNCは画面そのものを画像として送信しています。画像を送信する方法はRDPの方法より、重い一方、UbuntuやmacOSにはデフォルトでVNCを行うためのソフトが入っており、導入が比較的簡単です。RDPはMicrosoftが開発した方法で、画面の描画情報を送信(ウインドウの場所などの構成情報を送るイメージ)します。そのため、VNCと比べて軽いです。\nVNCを利用する Ubuntuには、Vinoと呼ばれるVNCサーバーが入っています。設定方法はここを参考にしました。 しかし、今回は2段階ssh環境でのVNCです。ターゲットに直接アクセスすることができません。そこでsshのポートフォワーディングを使います。これは、ターゲットの特定のポートをクライアントの任意のポートと接続できる機能です。先ほどのconfigファイルのtergetに以下を追記します。\n1 LocalForward 5900 localhost:5900 これでターゲットの5900ポートをクライアントの5900に接続することができました。 よって接続はlocalhost:5900に対して行います。 また、私の環境では、\n1$ gsettings set org.gnome.Vino require-encryption false を行って暗号化の設定をoffにしても暗号化が解除されませんでした。 設定を変更するdconfをインストールします。\n1$ sudo apt install dconf-editor ソフトを起動し、org.gnome.desktop.remote-access require-encryptionをfalseに設定します。これでもう一度接続すると接続することができました。VNCの暗号化をoffにしてしまってもssh自体が暗号化されているため、安全に通信することができます。逆に言えば、sshを使っていなければ暗号化を止めてしまったため危険です。\nRDPを利用する 1$ sudo apt install xrdp 2段階sshの環境でのRDPは上のVNCの場合と同様にポートフォワーディングが必要です。xrdpで利用するポートはデフォルトで、3389です。VNCの際と同じように設定を行います。\n1 LocalForward 3389 localhost:3389 localhost:3389にRDPクライアントを接続すれば、利用することができます。クライアントソフトについては、ここが参考になります。\n","permalink":"https://hattomo.github.io/posts/main/21/q3/0708-remote-access-settings/","summary":"\u003ch2 id=\"はじめに\"\u003eはじめに\u003c/h2\u003e\n\u003cp\u003eコンピューターにリモートアクセスを行いたいことがあります。この際の方法やTipsをまとめてみました。扱う方法はSSH,VNC,RDPです。\u003c/p\u003e\n\u003ch2 id=\"sshでリモート接続を行う\"\u003eSSHでリモート接続を行う\u003c/h2\u003e\n\u003cp\u003eSSHで外部からリモート接続を行う場合、セキュリティを考えると、多段階認証を行ったほうが良いです。多段階認証のサーバーは存在するとして、設定方法を記します。コンピュータの名前は以下のようにします。\u003c/p\u003e","title":"2段階SSH、VNC、RDPを行う方法"},{"content":"はじめに 先日、Ubuntuをapt update\u0026amp;apt upgradeをして再起動したら、見事にGeForceのドライバーが認識しなくなったので、その対応方法をメモします。\n環境 OS : Ubuntu20.04 GPU : Geforce 1080Ti\n症状 マルチモニター環境で、片方はGPU、もう片方は、マザーボードから出力しているが、GPUに接続している画面が映らなくなった。 もちろんnvidia-smiなどのコマンドもつかうことができなかった 起動時にEscキーを押してGrub Menuに入り、アップデート前のカーネルから起動するとGPUを認識し、普通に使うことができる。 対応方法 いろいろ試したのですが、結局ドライバーを再インストールし、再起動することで直りました。 以下はドライバーの再インストールの方法です。sudo apt purge nvidia-driver-xxxのみでアンインストールした場合は、うまくいきませんでした。\n1sudo apt update 2sudo apt upgrade 3 4# uninstall nvidia driver 5sudo apt purge nvidia-* 6sudo apt purge cuda-* 7 8# check driver 9ubuntu-drivers devices 10# install driver 11sudo ubuntu-drivers install 12# check GPU status 13nvidia-smi ","permalink":"https://hattomo.github.io/posts/main/21/q2/0416-fix-nvidia-driver-on-ubuntu/","summary":"\u003ch2 id=\"はじめに\"\u003eはじめに\u003c/h2\u003e\n\u003cp\u003e先日、\u003ccode\u003eUbuntu\u003c/code\u003eを\u003ccode\u003eapt update\u003c/code\u003e\u0026amp;\u003ccode\u003eapt upgrade\u003c/code\u003eをして再起動したら、見事にGeForceのドライバーが認識しなくなったので、その対応方法をメモします。\u003c/p\u003e","title":"UbuntuをアップデートしたらNvidia Driverが壊れた"},{"content":"はじめに 研究室のPCが与えられ、OSがUbuntuでした。Ubuntuでデフォルトで使えるキーバンドをカスタマイズしたので方法をまとめておきます。変更したキーバインドは以下のようです。\n変換、無変換で日本語、英語を切り替える emacs流のキーバインド(→,↓,←,↑,backspace,delete,home,end) CtrlとAltを入れ替える。 受け取った際、Mozcはすでにインストールされていました。追加でインストールしたのは、以下のソフトウェアです。autokey-gtkについては、aptでインストールされるものは古いので、GitHubのリーリスにあるものをインストールした方がいいというブログもありましたが、aptでインストールされるものとバージョンは同じだったので、aptでインストールを行いました。\n1$ sudo apt install gnome-tweak-tool autokey-gtk dconf-editor カスタマイズ! 変換、無変換で日本語、英語を切り替える これはMozcの設定で行いました。Mozcの設定を開き、henkan,muhenkanの枠をそれぞれ、IMEの有効化,IMEの無効化に設定します。Ubuntu18.04にて、半角 / 全角の切り替えをMac風に行なう方法と同じように行いました。\nemacs流のキーバインド emacs流のキーバインドを設定したのは、macを使っていたため、単に慣れていることと、CapsLockを押してしまい、入力設定が変化してしまうため、イライラしていたためです。\nこれを行うために、autokeyGitHubをインストールしました。x11環境向けに開発されているようでwaylandの環境では100%は動作しないようです。Ubuntu21.04からはwaylandがデフォルトになるようなのでwaylandでも使えるツールあるといいのですが、探したところ見つかりませんでした。autokeyをインストールしてからの設定はLinuxでMacっぽくCmd,Ctrlキーを使い分けるを参考にしました。しかし、hyper(CapsLock)+Pでウインドウの設定が起動してしまうため、このショートカットを無効にしました。この方法はstackoverflowにズバリな質問がありました。設定を行った後、再起動したところ反映されました。\nCtrlとAltを入れ替える Tweakで行いました。Keyboard \u0026amp; Mouse \u0026gt; Additional Layout Optionsから、Caps Lock behaviorを Make additional Hyperに Ctrl positionをSwap Left Alt with Left Ctrlに設定\nReference 上の文中であげさせていただきました。先人たちの知見に感謝します。\n","permalink":"https://hattomo.github.io/posts/main/21/q2/0413-ubuntu-keybinds/","summary":"\u003ch2 id=\"はじめに\"\u003eはじめに\u003c/h2\u003e\n\u003cp\u003e研究室のPCが与えられ、OSが\u003ccode\u003eUbuntu\u003c/code\u003eでした。\u003ccode\u003eUbuntu\u003c/code\u003eでデフォルトで使えるキーバンドをカスタマイズしたので方法をまとめておきます。変更したキーバインドは以下のようです。\u003c/p\u003e","title":"Ubuntuでキーバインドをカスタマイズする"},{"content":"はじめに Chromeでローカルにあるビデオや音声ファイルを開くと再生することができます。しかし、一時停止、再生、音量の変更、Picture in Pictureしかできません。再生速度とか変えたいですよね。そこで、Chrome Extensionを作って機能を拡張してみました。作成するエクステンションはスピードの変更(0.1倍速から16倍速まで)、5秒進む、5秒戻る、ループ設定(ON,OFF)、音量の変更、ミュート(ON,OFF)をキーボードショートカットで行えるようにするものです。やってみると意外と簡単でした。\nchrome extension を作成する エクステンションを作成していきます。ディレクトリを作成し、その中にファイルを作成していってください。\nmanifest.jsonの作成 chromeのextensionを作るためには、まずmanifest.jsonを作成する必要があります。これは、エクステンションの名称やバージョン、実行するファイルなどの設定を記述します。現在のmanifest_versionの最新は3なのでmanifest_versionを3としました。permissionsはエクステンションが必要な権限を記載します。content_scripts.matchesでは、このエクステンションがいつ有効になるかを設定します。URLがこれによって設定されているパターンとマッチしたときにエクステンションが有効になります。今回はfile://で始まると有効化されます。content_scripts.jsで実際に実行するファイルを指定します。iconsには、アイコンのパスを設定します。\n1{ 2 \u0026#34;manifest_version\u0026#34;: 3, 3 \u0026#34;name\u0026#34;: \u0026#34;Hattomo chrome media controller\u0026#34;, 4 \u0026#34;version\u0026#34;: \u0026#34;0.0.1\u0026#34;, 5 \u0026#34;description\u0026#34;: \u0026#34;Chrome video media controller!\u0026#34;, 6 \u0026#34;author\u0026#34;: \u0026#34;Hattomo\u0026#34;, 7 \u0026#34;permissions\u0026#34;: [ 8 \u0026#34;declarativeContent\u0026#34; 9 ], 10 \u0026#34;content_scripts\u0026#34;: [ 11 { 12 \u0026#34;matches\u0026#34;: [ 13 \u0026#34;file://*\u0026#34; 14 ], 15 \u0026#34;js\u0026#34;: [ 16 \u0026#34;content.js\u0026#34; 17 ] 18 } 19 ], 20 \u0026#34;icons\u0026#34;: { 21 \u0026#34;32\u0026#34;: \u0026#34;icons/favicon-32x32.png\u0026#34;, 22 \u0026#34;128\u0026#34;: \u0026#34;icons/apple-icon-180x180.png\u0026#34; 23 } 24} content.jsの設定 content.jsでは、videoタグがあるかを確認し、videoタグがあった場合、videoタグのidとしてvideo_idを指定します。videoタグがあるかを確認するのは画像などを開いた場合ビデオタグが見つからずのちの操作でエラーが出てしまうためです。その後、キーイベントのリスナーを登録し、イベントの発生を待ちます。動画や音声の操作は先ほど設定したidを使って行います。動画の速度や音量については、取れる値の幅に制限があるため、その制限を超えないよう実装します。また、iを押すとアラートダイアログがでて現在の速度などが表示されるようにしました。\n1let video_element = document.querySelector(\u0026#39;video\u0026#39;); 2if (video_element.id != null) { 3 4 video_element.id = \u0026#39;video_id\u0026#39;; 5 media = document.getElementById(\u0026#34;video_id\u0026#34;); 6 7 document.body.addEventListener(\u0026#39;keydown\u0026#39;, 8 event =\u0026gt; { 9 playbackRate = 0.1; 10 volumeRate = 0.05; 11 if (event.key === \u0026#39;f\u0026#39;) { 12 // fast 13 if (media.playbackRate + playbackRate \u0026lt; 16) { 14 media.playbackRate += 0.1 15 } else { 16 media.playbackRate = 16 17 } 18 console.log(\u0026#34;fast : \u0026#34; + media.playbackRate); 19 } else if (event.key === \u0026#39;s\u0026#39;) { 20 // slow 21 if (0.1 \u0026lt; media.playbackRate - playbackRate) { 22 media.playbackRate -= 0.1; 23 } else { 24 media.playbackRate = 0.1; 25 } 26 console.log(\u0026#34;slow : \u0026#34; + media.playbackRate); 27 } else if (event.key === \u0026#39;l\u0026#39;) { 28 // loop 29 if (media.loop) { 30 media.loop = false; 31 } else { 32 media.loop = true; 33 } 34 console.log(\u0026#34;loop : \u0026#34; + media.loop); 35 } else if (event.key === \u0026#39;m\u0026#39;) { 36 // mute 37 if (media.muted) { 38 media.muted = false; 39 } else { 40 media.muted = true; 41 } 42 console.log(\u0026#34;muted : \u0026#34; + media.loop); 43 } else if (event.key === \u0026#39;ArrowRight\u0026#39;) { 44 // skip 45 media.currentTime += 5; 46 } else if (event.key === \u0026#39;ArrowLeft\u0026#39;) { 47 // skip 48 media.currentTime -= 5; 49 } else if (event.key === \u0026#39;ArrowUp\u0026#39;) { 50 // volume 51 if (media.volume + volumeRate \u0026lt; 1) { 52 media.volume += 0.05; 53 } else { 54 media.volume = 1; 55 } 56 console.log(\u0026#34;volume : \u0026#34; + media.volume); 57 } else if (event.key === \u0026#39;ArrowDown\u0026#39;) { 58 if (0 \u0026lt; media.volume - volumeRate) { 59 media.volume -= 0.05; 60 } else { 61 media.volume = 0 62 } 63 console.log(\u0026#34;volume : \u0026#34; + media.volume); 64 } else if (event.key === \u0026#39;i\u0026#39;) { 65 msg = \u0026#34;Playback Speed \u0026#34; + media.playbackRate + \u0026#34;x\\nLoop : \u0026#34; + media.loop; 66 alert(msg); 67 } 68 }); 69} エクステンションを読み込み使ってみる chrome://extensions/にアクセスし、開発者モードを有効にした後、エクステンションを読み込むことで使い始めることができます。例えば、fを押して再生速度が速くなっていけば成功です。\nChromeには、現在英語のみですが、字幕の自動生成機能も新しくついたので、専用のビデオ再生ソフトよりもこれを使ったほうが便利になりそうです。\n","permalink":"https://hattomo.github.io/posts/main/21/q1/0313-chrome-extension/","summary":"\u003ch2 id=\"はじめに\"\u003eはじめに\u003c/h2\u003e\n\u003cp\u003eChromeでローカルにあるビデオや音声ファイルを開くと再生することができます。しかし、一時停止、再生、音量の変更、Picture in Pictureしかできません。再生速度とか変えたいですよね。そこで、Chrome Extensionを作って機能を拡張してみました。作成するエクステンションはスピードの変更(0.1倍速から16倍速まで)、5秒進む、5秒戻る、ループ設定(ON,OFF)、音量の変更、ミュート(ON,OFF)をキーボードショートカットで行えるようにするものです。やってみると意外と簡単でした。\u003c/p\u003e","title":"Chrome Extensionでメディアコントローラーを作る"},{"content":"はじめに YouTubeの動画をダウンロードするyoutube-dl(GitHub)を試してみました。\nインストール方法 GitHubのREADME.mdで最新の方法を確認してください。現時点では、バイナリをダウンロードする方法、pipでインストールする方法、brewなどがあるようです。インストールした後、パスを通してください。\nyoutube-dlの使い方 youtube-dlは、\n1$ youtube-dl [URL] で簡単に使うことができます。しかし、保存場所や画質のオプションも含めると何度もコマンドを記述するのは大変なのでシャルスクリプトを作成します。今回利用するオプションは保存場所の指定、画質設定、字幕とサムネイルをダウンロードするの4つです。\n保存場所の指定のオプション 保存場所を指定するには-o [path to file]を利用します。下のように指定することでファイルのタイトル、チャンネル名、動画のidなどの情報をファイル名に含めることができます。\n1-o $D_PATH\u0026#39;/%(title)s-%(uploader)s-%(id)s.%(ext)s\u0026#39; $URLs 画質と音質のオプション 画質と音質はデフォルトではbestがダウンロードされます。これは、720pであることが多いようです。しかし、画質と音声を指定した質でダウンロードすることもできます。この場合画質と音声は、それぞれ別にダウンロードすることになり、あとで自動的に結合されます。結合にはffmpegかavconvが必要です。今回はffmpegをapt insatall ffmpegによってインストールしました。画質のと音声のオプションは以下のように-fオプションで行いました。\n1-f bestvideo[height=$Image_Quality][ext=mp4]+bestaudio[ext=m4a]/bestvideo[height=1080][ext=mp4]+bestaudio[ext=m4a]/bestvideo[height=720][ext=mp4]+bestaudio[ext=m4a]/best 字幕をダウンロードするオプション 1--write-sub サムネイルをダウンロードするオプション 1--write-thumbnail シェルスクリプト 全体のシェルスクリプトです。URLと画質をコマンドライン引数として指定して、ダウンロードすることができます。\n1URLs=$1 2if [ \u0026#34;$2\u0026#34; != \u0026#34;\u0026#34; ]; then 3 Image_Quality=$2 4else 5 Image_Quality=1080 6fi 7echo \u0026#34;Image_Quality : \u0026#34;$Image_Quality 8echo -e \u0026#34;URLs : \u0026#34;$URLs\u0026#34;\\n\u0026#34; 9D_PATH=[file save path] 10 11### downloads 12youtube-dl -f bestvideo[height=$Image_Quality][ext=mp4]+bestaudio[ext=m4a]/bestvideo[height=1080][ext=mp4]+bestaudio[ext=m4a]/bestvideo[height=720][ext=mp4]+bestaudio[ext=m4a]/best \\ 13--merge-output-format mp4 \\ 14--write-sub \\ 15--write-thumbnail \\ 16-o $D_PATH\u0026#39;/%(title)s-%(uploader)s-%(id)s.%(ext)s\u0026#39; $URLs 17 18### check update and update if update available 19echo -e \u0026#34;\\nchecking update ...\u0026#34; 20youtube-dl -U 時刻を指定してyoutubeをダウンロードする ライブストリーミングや動画がプレミア公開された直後にダウンロードしたいときもあるでしょう。時刻を指定してダウンロードするためには、atコマンドを利用します。1行目でatコマンドを利用するためのデーモンを起動します。2行では、日時を指定しており、3行目はスクリプトを実行しています。atコマンドからは、ctrl+Dで脱出することができ、\u0026lt;EOT\u0026gt;と表示されます。この際、標準出力やエラーをターミナルにリダイレクトするための設定が、\u0026gt; /dev/pts/0 2\u0026gt;\u0026amp;1の部分です。/dev/pts/0はご利用の境に合わせて、ttyコマンドなどで確認してください。設定を確認するためには、at -lを利用します。\n1$ sudo /etc/init.d/atd start 2$ at 17:05 03/11/2021 3at\u0026gt; youtube.sh URL \u0026gt; /dev/pts/0 2\u0026gt;\u0026amp;1 4at\u0026gt; \u0026lt;EOT\u0026gt; 5$ at -l ","permalink":"https://hattomo.github.io/posts/main/21/q1/0312-youtube/","summary":"\u003ch2 id=\"はじめに\"\u003eはじめに\u003c/h2\u003e\n\u003cp\u003eYouTubeの動画をダウンロードする\u003ccode\u003eyoutube-dl\u003c/code\u003e\u003ca href=\"https://github.com/ytdl-org/youtube-dl/\"\u003e(GitHub)\u003c/a\u003eを試してみました。\u003c/p\u003e\n\u003ch2 id=\"インストール方法\"\u003eインストール方法\u003c/h2\u003e\n\u003cp\u003eGitHubの\u003ca href=\"https://github.com/ytdl-org/youtube-dl/\"\u003eREADME.md\u003c/a\u003eで最新の方法を確認してください。現時点では、バイナリをダウンロードする方法、\u003ccode\u003epip\u003c/code\u003eでインストールする方法、\u003ccode\u003ebrew\u003c/code\u003eなどがあるようです。インストールした後、パスを通してください。\u003c/p\u003e\n\u003ch2 id=\"youtube-dlの使い方\"\u003eyoutube-dlの使い方\u003c/h2\u003e\n\u003cp\u003eyoutube-dlは、\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-sh\" data-lang=\"sh\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"ln\"\u003e1\u003c/span\u003e\u003cspan class=\"cl\"\u003e$ youtube-dl \u003cspan class=\"o\"\u003e[\u003c/span\u003eURL\u003cspan class=\"o\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eで簡単に使うことができます。しかし、保存場所や画質のオプションも含めると何度もコマンドを記述するのは大変なのでシャルスクリプトを作成します。今回利用するオプションは保存場所の指定、画質設定、字幕とサムネイルをダウンロードするの4つです。\u003c/p\u003e","title":"Youtubeの動画をダウンロードするyoutube-dlを試す"},{"content":"はじめに Lineには、トーク履歴をエクスポートする機能が付いています。これをPythonを使って解析し、合計メッセージ数、それぞれのメッセージ数、合計文字数、それぞれのメッセージ数、Line電話の時間の合計をそれぞれの月について算出する方法です。筆者は、電子機器の言語を英語に設定しているため、日本語を使用されている方は、履歴のファイル名や内容が日本語表記になっていることが予想されます。適宜読み替えてください。\nLineからトーク履歴をエクスポートする これは、PCでもスマホでもできますが、PCとスマホでは、エクスポートされたトーク履歴のフォーマットが微妙に違うことやPCでは、エクスポートできるトーク履歴が会話全体の一部でしかないため、今回はスマホでエクスポートし、PCに送りました。\nトーク履歴をCSVに変換する エクスポートされたファイルは、[LINE] Chat with [friend name].txtとなっていました。フォーマットは以下のようでした。うーん、このフォーマットは使いにくい気が\u0026hellip;\n1~略~ 22021/03/03 Wed 310:15\tfrinds account name 次の電話は明日の18時半がいいです。 411:37\tmy account name [Sticker] 5~略~ 日時、タブ、アカウント名、タブ、メッセージとなっています。 Lineには様々な機能が付いています。テキストメッセージ、写真、動画、スタンプ、電話、アルバム、メッセージの取り消しなどのシステムメッセージ\u0026hellip;。これらは履歴の中では、特定の形で表現されているので、それぞれの履歴がどの種類のメッセージであるかを正規表現を使って分け、csvに変換していきます。csvの形式は年,月,日,時,分,送信者,内容,flagとします。flagについては後述します。\n履歴ファイルを読み込む 履歴ファイルを読み込みます。\n1file_path = \u0026#34;[LINE] Chat with [freind name].txt\u0026#34; 2with open(file_path, \u0026#39;r\u0026#39;, encoding=\u0026#34;utf-8\u0026#34;) as f: 3 log_text = f.read() 正規表現を設定する それぞれのメッセージ種別について正規表現を設定していきます。\n1# 日時データ 2date_pattern = r\u0026#34;20\\d{2}/\\d{2}/\\d{2} (Mon|Tue|Wed|Thu|Fri|Sat|Sun)\u0026#34; 3# テキストメッセージデータ 4message_pattern = r\u0026#34;\\d{2}:\\d{2}\\t.*\\t.*\u0026#34; 5# 写真のデータ 6photo_pattern = r\u0026#34;\\d{2}:\\d{2}\\t.*\\t\\[Photo]\u0026#34; 7# スタンプのデータ 8sticker_pattern = r\u0026#34;\\d{2}:\\d{2}\\t.*\\t\\[Sticker]\u0026#34; 9# ビデオデータ 10video_pattern = r\u0026#34;\\d{2}:\\d{2}\\t.*\\t\\[Video]\u0026#34; 11# ファイルのデータ 12file_pattern = r\u0026#34;\\d{2}:\\d{2}\\t.*\\t\\[File]\u0026#34; 13# アルバム作成、名前変更、削除のデータ 14album_build_pattern = r\u0026#34;\\d{2}:\\d{2}\\t.*\\t\\[Albums].*\u0026#34; 15album_rename_pattern = r\u0026#34;\\d{2}:\\d{2}\\t.* changed the name of the album.*\u0026#34; 16album_delete_pattern = r\u0026#34;\\d{2}:\\d{2}\\t.* delete the album.*\u0026#34; 17# 電話のデータ関係 18missed_call_pattern = r\u0026#34;\\d{2}:\\d{2}\\t.*\\t☎ Missed call\u0026#34; 19canceled_call_pattern = r\u0026#34;\\d{2}:\\d{2}\\t.*\\t☎ Canceled call\u0026#34; 20no_answer_call_pattern = r\u0026#34;\\d{2}:\\d{2}\\t.*\\t☎ No answer\u0026#34; 21call_pattern = r\u0026#34;\\d{2}:\\d{2}\\t.*\\t☎ Call time (\\d{1,2}:\\d{2}|\\d{1,2}:\\d{2}:\\d{2})\u0026#34; 22# システムのデータ、送信取り消し 23sys_unsent_pattern = r\u0026#34;\\d{2}:\\d{2}\\t.* unsent a message.\u0026#34; データを正規表現に沿って解析する re.match()で正規表現に当てはまっているかを確認し、当てはまっていたら、タブでデータを分割したのち、Data型にデータを格納し、リストlogに追加していきます。データ型のflagという変数はデータの種類を示しており、以下のように設定しています。\n1# flag 2 # 0 : talk meassge 3 # 10 : call 4 # 11 : missed call 5 # 12 : canceled call 6 # 13 : no answer call 7 # 2 : photo 8 # 3 : video 9 # 4 : sticker 10 # 50 : system message unsent 11 # 60 : file 12 # 70 : create and add album 13 # 71 : changed the name of the album 14 # 72 : deleted the album 1class Data(): 2 def __init__(self, year, month, day, hour, minute, person, payload, flag): 3 self.year = year 4 self.month = month 5 self.day = day 6 self.hour = hour 7 self.minute = minute 8 self.person = person 9 self.payload = payload 10 self.flag = flag 11 12date_ = datetime.datetime.now() 13logs = [] 14 15# 履歴の最初の2行はエクスポートした時間と空白の行なのでとばし、3行目から解析する 16for i, log in enumerate(log_text.splitlines()[3:]): 17 #print(f\u0026#34;{log} : \u0026#34;, end=\u0026#39;\u0026#39;) 18 if log == \u0026#39;\u0026#39;: 19 #print(\u0026#34;no data\u0026#34;) 20 continue 21 date_stamp = \u0026#34;\u0026#34; 22 if re.match(date_pattern, log): 23 #print(\u0026#34;day data\u0026#34;) 24 date_stamp = log.replace(\u0026#39;/\u0026#39;, \u0026#39;,\u0026#39;).replace(\u0026#39; \u0026#39;, \u0026#39;,\u0026#39;)[0:10] 25 date_ = datetime.datetime.strptime(date_stamp, \u0026#39;%Y,%m,%d\u0026#39;) 26 elif re.match(photo_pattern, log): 27 #print(\u0026#34;photo data\u0026#34;) 28 splited_log = re.split(\u0026#39;\\t\u0026#39;, log) 29 logs.append(Data(date_.year, date_.month, date_.day, 30 splited_log[0][0:2], splited_log[0][3:5], splited_log[1], \u0026#34;\u0026#34;, 2)) 31 elif re.match(video_pattern, log): 32 #print(\u0026#34;Video data\u0026#34;) 33 splited_log = re.split(\u0026#39;\\t\u0026#39;, log) 34 logs.append(Data(date_.year, date_.month, date_.day, 35 splited_log[0][0:2], splited_log[0][3:5], splited_log[1], \u0026#34;\u0026#34;, 3)) 36 # ~略~ csvを保存する 以下のコードでcsvファイルを保存します。line.csvというファイルに保存されます。\n1with open(\u0026#39;line.csv\u0026#39;, \u0026#39;w\u0026#39;, encoding=\u0026#34;utf-8\u0026#34;, newline=\u0026#34;\u0026#34;) as f: 2 for content in logs: 3 writer = csv.writer(f) 4 writer.writerow([str(content.year), str(content.month), str(content.day), str(content.hour),str(content.minute), str(content.person), str(content.payload), str(content.flag)]) トーク履歴をCSVに変換するコードの全体 ここまでのコードの全体です\n1# -*- coding: utf-8 -*- 2 3import re 4import csv 5import datetime 6import os 7import sys 8 9 10class Data(): 11 # flag 12 # 0 : talk meassge 13 # 10 : call 14 # 11 : missed call 15 # 12 : canceled call 16 # 13 : no answer call 17 # 2 : photo 18 # 3 : video 19 # 4 : sticker 20 # 50 : system message unsent 21 # 60 : file 22 # 70 : create and add album 23 # 71 : changed the name of the album 24 # 72 : deleted the album 25 def __init__(self, year, month, day, hour, minute, person, payload, flag): 26 self.year = year 27 self.month = month 28 self.day = day 29 self.hour = hour 30 self.minute = minute 31 self.person = person 32 self.payload = payload 33 self.flag = flag 34 35 36# disable #print 37# sys.stdout = open(os.devnull, \u0026#39;w\u0026#39;, encoding=\u0026#34;utf-8\u0026#34;) 38 39file_path = \u0026#34;[LINE] Chat with friend.txt\u0026#34; 40 41date_ = datetime.datetime.now() 42logs = [] 43 44# open file and load data 45with open(file_path, \u0026#39;r\u0026#39;, encoding=\u0026#34;utf-8\u0026#34;) as f: 46 log_text = f.read() 47 48date_pattern = r\u0026#34;20\\d{2}/\\d{2}/\\d{2} (Mon|Tue|Wed|Thu|Fri|Sat|Sun)\u0026#34; 49message_pattern = r\u0026#34;\\d{2}:\\d{2}\\t.*\\t.*\u0026#34; 50photo_pattern = r\u0026#34;\\d{2}:\\d{2}\\t.*\\t\\[Photo]\u0026#34; 51sticker_pattern = r\u0026#34;\\d{2}:\\d{2}\\t.*\\t\\[Sticker]\u0026#34; 52video_pattern = r\u0026#34;\\d{2}:\\d{2}\\t.*\\t\\[Video]\u0026#34; 53file_pattern = r\u0026#34;\\d{2}:\\d{2}\\t.*\\t\\[File]\u0026#34; 54album_build_pattern = r\u0026#34;\\d{2}:\\d{2}\\t.*\\t\\[Albums].*\u0026#34; 55album_rename_pattern = r\u0026#34;\\d{2}:\\d{2}\\t.* changed the name of the album.*\u0026#34; 56album_delete_pattern = r\u0026#34;\\d{2}:\\d{2}\\t.* delete the album.*\u0026#34; 57missed_call_pattern = r\u0026#34;\\d{2}:\\d{2}\\t.*\\t☎ Missed call\u0026#34; 58canceled_call_pattern = r\u0026#34;\\d{2}:\\d{2}\\t.*\\t☎ Canceled call\u0026#34; 59no_answer_call_pattern = r\u0026#34;\\d{2}:\\d{2}\\t.*\\t☎ No answer\u0026#34; 60call_pattern = r\u0026#34;\\d{2}:\\d{2}\\t.*\\t☎ Call time (\\d{1,2}:\\d{2}|\\d{1,2}:\\d{2}:\\d{2})\u0026#34; 61sys_unsent_pattern = r\u0026#34;\\d{2}:\\d{2}\\t.* unsent a message.\u0026#34; 62 63for i, log in enumerate(log_text.splitlines()[3:]): 64 #print(f\u0026#34;{log} : \u0026#34;, end=\u0026#39;\u0026#39;) 65 if log == \u0026#39;\u0026#39;: 66 #print(\u0026#34;no data\u0026#34;) 67 continue 68 date_stamp = \u0026#34;\u0026#34; 69 if re.match(date_pattern, log): 70 #print(\u0026#34;day data\u0026#34;) 71 date_stamp = log.replace(\u0026#39;/\u0026#39;, \u0026#39;,\u0026#39;).replace(\u0026#39; \u0026#39;, \u0026#39;,\u0026#39;)[0:10] 72 date_ = datetime.datetime.strptime(date_stamp, \u0026#39;%Y,%m,%d\u0026#39;) 73 elif re.match(photo_pattern, log): 74 #print(\u0026#34;photo data\u0026#34;) 75 splited_log = re.split(\u0026#39;\\t\u0026#39;, log) 76 logs.append(Data(date_.year, date_.month, date_.day, 77 splited_log[0][0:2], splited_log[0][3:5], splited_log[1], \u0026#34;\u0026#34;, 2)) 78 elif re.match(video_pattern, log): 79 #print(\u0026#34;Video data\u0026#34;) 80 splited_log = re.split(\u0026#39;\\t\u0026#39;, log) 81 logs.append(Data(date_.year, date_.month, date_.day, 82 splited_log[0][0:2], splited_log[0][3:5], splited_log[1], \u0026#34;\u0026#34;, 3)) 83 elif re.match(sticker_pattern, log): 84 #print(\u0026#34;Sticker data\u0026#34;) 85 splited_log = re.split(\u0026#39;\\t\u0026#39;, log) 86 logs.append(Data(date_.year, date_.month, date_.day, 87 splited_log[0][0:2], splited_log[0][3:5], splited_log[1], \u0026#34;\u0026#34;, 4)) 88 elif re.match(call_pattern, log): 89 #print(\u0026#34;call data\u0026#34;) 90 splited_log = re.split(\u0026#39;\\t\u0026#39;, log) 91 time_data = splited_log[2][12:] 92 time_data = re.split(\u0026#39;:\u0026#39;, time_data) 93 time_length = 0 94 for i in range(len(time_data)): 95 time_length += int(time_data[len(time_data) - i - 1]) * (60 ** i) 96 # print(time_length) 97 logs.append(Data(date_.year, date_.month, date_.day, 98 splited_log[0][0:2], splited_log[0][3:5], splited_log[1], time_length, 10)) 99 elif re.match(missed_call_pattern, log): 100 #print(\u0026#34;Missed call data\u0026#34;) 101 splited_log = re.split(\u0026#39;\\t\u0026#39;, log) 102 logs.append(Data(date_.year, date_.month, date_.day, 103 splited_log[0][0:2], splited_log[0][3:5], splited_log[1], \u0026#34;\u0026#34;, 11)) 104 elif re.match(canceled_call_pattern, log): 105 #print(\u0026#34;Canceled call data\u0026#34;) 106 splited_log = re.split(\u0026#39;\\t\u0026#39;, log) 107 logs.append(Data(date_.year, date_.month, date_.day, 108 splited_log[0][0:2], splited_log[0][3:5], splited_log[1], \u0026#34;\u0026#34;, 12)) 109 elif re.match(no_answer_call_pattern, log): 110 #print(\u0026#34;no answer call data\u0026#34;) 111 splited_log = re.split(\u0026#39;\\t\u0026#39;, log) 112 logs.append(Data(date_.year, date_.month, date_.day, 113 splited_log[0][0:2], splited_log[0][3:5], splited_log[1], \u0026#34;\u0026#34;, 13)) 114 elif re.match(sys_unsent_pattern, log): 115 #print(\u0026#34;sys unsent data\u0026#34;) 116 splited_log = re.split(\u0026#39;\\t\u0026#39;, log) 117 logs.append(Data(date_.year, date_.month, date_.day, 118 splited_log[0][0:2], splited_log[0][3:5], \u0026#34;\u0026#34;, \u0026#34;\u0026#34;, 50)) 119 elif re.match(file_pattern, log): 120 #print(\u0026#34;file data\u0026#34;) 121 splited_log = re.split(\u0026#39;\\t\u0026#39;, log) 122 logs.append(Data(date_.year, date_.month, date_.day, 123 splited_log[0][0:2], splited_log[0][3:5], \u0026#34;\u0026#34;, \u0026#34;\u0026#34;, 60)) 124 elif re.match(album_build_pattern, log): 125 #print(\u0026#34;create album data\u0026#34;) 126 splited_log = re.split(\u0026#39;\\t\u0026#39;, log) 127 logs.append(Data(date_.year, date_.month, date_.day, 128 splited_log[0][0:2], splited_log[0][3:5], \u0026#34;\u0026#34;, \u0026#34;\u0026#34;, 70)) 129 elif re.match(album_rename_pattern, log): 130 #print(\u0026#34;rename album data\u0026#34;) 131 splited_log = re.split(\u0026#39;\\t\u0026#39;, log) 132 logs.append(Data(date_.year, date_.month, date_.day, 133 splited_log[0][0:2], splited_log[0][3:5], \u0026#34;\u0026#34;, \u0026#34;\u0026#34;,71)) 134 elif re.match(album_delete_pattern, log): 135 #print(\u0026#34;delete album data\u0026#34;) 136 splited_log = re.split(\u0026#39;\\t\u0026#39;, log) 137 logs.append(Data(date_.year, date_.month, date_.day, 138 splited_log[0][0:2], splited_log[0][3:5], \u0026#34;\u0026#34;, \u0026#34;\u0026#34;, 72)) 139 elif re.match(message_pattern, log): 140 #print(\u0026#34;message data\u0026#34;) 141 splited_log = re.split(\u0026#39;\\t\u0026#39;, log) 142 logs.append(Data(date_.year, date_.month, date_.day, 143 splited_log[0][0:2], splited_log[0][3:5], splited_log[1], splited_log[2], 0)) 144 elif (len(re.split(\u0026#39;\\t\u0026#39;, log)) == 1): 145 splited_log = re.split(\u0026#39;\\t\u0026#39;, log) 146 #print(\u0026#34;returned data\u0026#34;) 147 logs[-1].payload += log 148 else: 149 pass 150 #print(\u0026#34;\\nNo classified data\\n\u0026#34;) 151 152with open(\u0026#39;line.csv\u0026#39;, \u0026#39;w\u0026#39;, encoding=\u0026#34;utf-8\u0026#34;, newline=\u0026#34;\u0026#34;) as f: 153 for content in logs: 154 writer = csv.writer(f) 155 writer.writerow([str(content.year), str(content.month), str(content.day), str(content.hour),str(content.minute), str(content.person), str(content.payload), str(content.flag)]) 156 157print(\u0026#34;Success🎉\u0026#34;) CSVを解析するし、グラフを生成する CSVの解析にはpandas、グラフの生成にはmatplotlibをpandasのラッパーを通して利用してます。ラッパーなので生成されるグラフはmatplotlibそのものです。\nCSVを読み込む CSVをpandasを利用して読み込みます\n1file_path = \u0026#34;line.csv\u0026#34; 2df = pd.read_csv(file_path, names=(\u0026#39;year\u0026#39;, \u0026#39;month\u0026#39;, \u0026#39;day\u0026#39;, 3 \u0026#39;hour\u0026#39;, \u0026#39;minute\u0026#39;, \u0026#39;person\u0026#39;, \u0026#39;payloads\u0026#39;, \u0026#39;flag\u0026#39;), encoding=\u0026#34;UTF-8\u0026#34;) 全体の月別メッセージ数 pandasのgroupby機能によって月ごとのメッセージ数を数えます。これをグラフにします。ほかのデータを解析する場合も基本は同様です。これを少し変えることで、曜日別や時間別なども簡単に作ることができそうです。\n1month_message = df[[\u0026#34;year\u0026#34;, \u0026#34;month\u0026#34;, \u0026#34;flag\u0026#34;] 2 ].groupby([\u0026#39;year\u0026#39;, \u0026#39;month\u0026#39;]).count() 3 4month_message.plot(y=\u0026#39;flag\u0026#39;, kind=\u0026#39;bar\u0026#39;, label=\u0026#34;count\u0026#34;, figsize=figsize) 5plt.ylabel(\u0026#34;message count\u0026#34;) 6plt.legend() 7plt.ylim(0,) 8plt.title(\u0026#39;message\u0026#39;) 9plt.savefig(\u0026#39;message_count.png\u0026#39;) 人ごとの月別メッセージ数 pandasでは、クロスタブを利用することで、簡単にクロス集計分析を行うことができます\n1person_month_message = pd.crosstab([df[\u0026#39;year\u0026#39;], df[\u0026#34;month\u0026#34;]], df[\u0026#39;person\u0026#39;]) 2person_month_message.plot(kind=\u0026#39;line\u0026#39;, figsize=figsize) 3plt.title(\u0026#34;message count by person\u0026#34;) 4plt.ylabel(\u0026#34;count\u0026#34;) 5plt.ylim(0,) 6plt.savefig(\u0026#39;message_count_by_person.png\u0026#39;) 月別電話時間 電話のflagは10なのでまずは、csvからそのデータを取り出します。さらに電話時間はCSVに秒で記録されておりそのままでは、値が大きく理解しずらいため、3600でわり、時間にしました。\n1call_time = df[df[\u0026#39;flag\u0026#39;] == 10] 2call_time = call_time[[\u0026#34;year\u0026#34;, \u0026#34;month\u0026#34;, \u0026#34;payloads\u0026#34;]] 3call_time = call_time.astype(\u0026#39;int64\u0026#39;).groupby([\u0026#39;year\u0026#39;, \u0026#39;month\u0026#39;]).sum() / 3600 4call_time.plot(y=\u0026#39;payloads\u0026#39;, kind=\u0026#39;bar\u0026#39;, label=\u0026#39;time\u0026#39;, figsize=figsize) 5plt.ylabel(\u0026#34;time(hours)\u0026#34;) 6plt.legend() 7plt.ylim(0,) 8plt.title(\u0026#39;Call Time\u0026#39;) 9plt.savefig(\u0026#39;call_time.png\u0026#39;) 月別メッセージの文字数の合計 charというcolumnを作成し、そこにメッセージの文字数を入れたあとメッセージ数を数えた時と同じように、集計を行いました。\n1# メッセージを取り出す 2char_count_data = df[df[\u0026#39;flag\u0026#39;] == 0] 3# charというcolumnを作成し、そこにメッセージの文字数を入れる 4char_count_data[\u0026#34;char\u0026#34;] = char_count_data[\u0026#34;payloads\u0026#34;].apply(lambda x: len(x)) 5char_count = char_count_data[[\u0026#34;year\u0026#34;, \u0026#34;month\u0026#34;, \u0026#34;char\u0026#34;]] 6char_count = char_count.astype(\u0026#39;int64\u0026#39;).groupby([\u0026#39;year\u0026#39;, \u0026#39;month\u0026#39;]).sum() 7char_count.plot(y=\u0026#39;char\u0026#39;, kind=\u0026#39;bar\u0026#39;, label=\u0026#39;char\u0026#39;, figsize=figsize) 8plt.ylabel(\u0026#34;char\u0026#34;) 9plt.legend() 10plt.ylim(0,) 11plt.title(\u0026#39;char data\u0026#39;) 12plt.savefig(\u0026#39;char_count.png\u0026#39;) 人べつ月ごとの文字数の合計 月別メッセージの文字数の合計で作成したデータフレームを再利用し人べつのデータを作成します。\n1char_count_by_person = char_count_data[[\u0026#34;year\u0026#34;, \u0026#34;month\u0026#34;, \u0026#34;person\u0026#34;, \u0026#34;char\u0026#34;]] 2char_count_by_person[\u0026#34;char\u0026#34;].astype(\u0026#39;int64\u0026#39;) 3char_count_by_person = pd.pivot_table( 4 char_count_by_person, 5 values=\u0026#34;char\u0026#34;, 6 index=[\u0026#34;year\u0026#34;, \u0026#34;month\u0026#34;], 7 columns=\u0026#34;person\u0026#34;, 8 aggfunc=\u0026#34;sum\u0026#34; 9) 10char_count_by_person.plot(kind=\u0026#39;line\u0026#39;, figsize=figsize) 11plt.ylabel(\u0026#34;char\u0026#34;) 12plt.title(\u0026#39;Char by person\u0026#39;) 13plt.ylim(0,) 14plt.legend() 15plt.savefig(\u0026#39;char_count_by_person.png\u0026#39;) 解析するコードの全体 1# -*- coding: utf-8 -*- 2 3import pandas as pd 4import matplotlib.pyplot as plt 5 6file_path = \u0026#34;line.csv\u0026#34; 7 8figsize = (12, 8) 9df = pd.read_csv(file_path, names=(\u0026#39;year\u0026#39;, \u0026#39;month\u0026#39;, \u0026#39;day\u0026#39;, 10 \u0026#39;hour\u0026#39;, \u0026#39;minute\u0026#39;, \u0026#39;person\u0026#39;, \u0026#39;payloads\u0026#39;, \u0026#39;flag\u0026#39;), encoding=\u0026#34;UTF-8\u0026#34;) 11month_message = df[[\u0026#34;year\u0026#34;, \u0026#34;month\u0026#34;, \u0026#34;flag\u0026#34;] 12 ].groupby([\u0026#39;year\u0026#39;, \u0026#39;month\u0026#39;]).count() 13 14month_message.plot(y=\u0026#39;flag\u0026#39;, kind=\u0026#39;bar\u0026#39;, label=\u0026#34;count\u0026#34;, figsize=figsize) 15plt.ylabel(\u0026#34;message count\u0026#34;) 16plt.legend() 17plt.ylim(0,) 18plt.title(\u0026#39;message\u0026#39;) 19plt.savefig(\u0026#39;message_count.png\u0026#39;) 20 21person_month_message = pd.crosstab([df[\u0026#39;year\u0026#39;], df[\u0026#34;month\u0026#34;]], df[\u0026#39;person\u0026#39;]) 22person_month_message.plot(kind=\u0026#39;line\u0026#39;, figsize=figsize) 23plt.title(\u0026#34;message count by person\u0026#34;) 24plt.ylabel(\u0026#34;count\u0026#34;) 25plt.ylim(0,) 26plt.savefig(\u0026#39;message_count_by_person.png\u0026#39;) 27 28call_time = df[df[\u0026#39;flag\u0026#39;] == 10] 29call_time = call_time[[\u0026#34;year\u0026#34;, \u0026#34;month\u0026#34;, \u0026#34;payloads\u0026#34;]] 30newdf = pd.DataFrame([[2018, 3, 0], [2018, 10, 0], [2018, 12, 0], [2019, 1, 0], [2019, 3, 0], [2019, 5, 0], [2020, 10, 0], [2020, 11, 0], [2021, 2, 0], ], 31 columns=[\u0026#34;year\u0026#34;, \u0026#34;month\u0026#34;, \u0026#34;payloads\u0026#34;]) 32call_time.append(newdf, ignore_index=True) 33call_time = call_time.append(newdf) 34call_time = call_time.astype(\u0026#39;int64\u0026#39;).groupby([\u0026#39;year\u0026#39;, \u0026#39;month\u0026#39;]).sum() / 3600 35call_time.plot(y=\u0026#39;payloads\u0026#39;, kind=\u0026#39;bar\u0026#39;, label=\u0026#39;time\u0026#39;, figsize=figsize) 36plt.ylabel(\u0026#34;time(hours)\u0026#34;) 37plt.legend() 38plt.ylim(0,) 39plt.title(\u0026#39;Call Time\u0026#39;) 40plt.savefig(\u0026#39;call_time.png\u0026#39;) 41 42char_count_data = df[df[\u0026#39;flag\u0026#39;] == 0] 43char_count_data[\u0026#34;char\u0026#34;] = char_count_data[\u0026#34;payloads\u0026#34;].apply(lambda x: len(x)) 44char_count = char_count_data[[\u0026#34;year\u0026#34;, \u0026#34;month\u0026#34;, \u0026#34;char\u0026#34;]] 45char_count = char_count.astype(\u0026#39;int64\u0026#39;).groupby([\u0026#39;year\u0026#39;, \u0026#39;month\u0026#39;]).sum() 46char_count.plot(y=\u0026#39;char\u0026#39;, kind=\u0026#39;bar\u0026#39;, label=\u0026#39;char\u0026#39;, figsize=figsize) 47plt.ylabel(\u0026#34;char\u0026#34;) 48plt.legend() 49plt.ylim(0,) 50plt.title(\u0026#39;char data\u0026#39;) 51plt.savefig(\u0026#39;char_count.png\u0026#39;) 52 53char_count_by_person = char_count_data[[\u0026#34;year\u0026#34;, \u0026#34;month\u0026#34;, \u0026#34;person\u0026#34;, \u0026#34;char\u0026#34;]] 54char_count_by_person[\u0026#34;char\u0026#34;].astype(\u0026#39;int64\u0026#39;) 55char_count_by_person = pd.pivot_table( 56 char_count_by_person, 57 values=\u0026#34;char\u0026#34;, 58 index=[\u0026#34;year\u0026#34;, \u0026#34;month\u0026#34;], 59 columns=\u0026#34;person\u0026#34;, 60 aggfunc=\u0026#34;sum\u0026#34; 61) 62char_count_by_person.plot(kind=\u0026#39;line\u0026#39;, figsize=figsize) 63plt.ylabel(\u0026#34;char\u0026#34;) 64plt.title(\u0026#39;Char by person\u0026#39;) 65plt.ylim(0,) 66plt.legend() 67plt.savefig(\u0026#39;char_count_by_person.png\u0026#39;) 以上でグラフを作成することができました。一度作ってしまえば実行するだけなので、定期的に実行して変化を試したいと思います。\nReference 以下のページを参考にしました\nhttps://qiita.com/shimajiroxyz/items/9a06a086ee9730ee3d55 ","permalink":"https://hattomo.github.io/posts/main/21/q1/0304-python-line/","summary":"\u003ch2 id=\"はじめに\"\u003eはじめに\u003c/h2\u003e\n\u003cp\u003eLineには、トーク履歴をエクスポートする機能が付いています。これをPythonを使って解析し、合計メッセージ数、それぞれのメッセージ数、合計文字数、それぞれのメッセージ数、Line電話の時間の合計をそれぞれの月について算出する方法です。筆者は、電子機器の言語を英語に設定しているため、日本語を使用されている方は、履歴のファイル名や内容が日本語表記になっていることが予想されます。適宜読み替えてください。\u003c/p\u003e","title":"PythonでLINEトークの履歴を解析し、グラフを生成する"},{"content":"はじめに OSはどのように動いているのか気になり、自作をしてみることにしました。基本の用語から何もわからん\u0026hellip;。 Rust+RISC-V でいきたい\u0026hellip;\ndocker dockerをインストールし、ubuntu:2004を利用します。すべての開発はdocker上で行います。\nBuild qemu qemuをビルドします。最初に必要なものをapt installします。\n1sudo apt install autoconf automake autotools-dev curl libmpc-dev libmpfr-dev libgmp-dev \\ 2 gawk build-essential bison flex texinfo gperf libtool patchutils bc \\ 3 zlib1g-dev libexpat-dev git 1git clone https://github.com/qemu/qemu 2cd qemu 3git checkout v5.2.0 4./configure --target-list=riscv64-softmmu 5make -j $(nproc) 6sudo make install 途中、ninjaがないと怒られたので下のものを追加しました。\n1sudo -i 2curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py 3python3 get-pip.py 4python3 -m pip install ninja 5exit Rustのインストール とりあえず、通常版です。\n1$ curl --proto \u0026#39;=https\u0026#39; --tlsv1.2 -sSf https://sh.rustup.rs | sh 2$ rustup target add riscv64gc-unknown-none-elf 3$ rustc --version 4rustc 1.50.0 (cb75ad5db 2021-02-10) risc-v向けgccのインストール 1sudo apt install gcc-riscv64-unknown-elf 確認 1$ qemu-system-riscv64 --version 2QEMU emulator version 5.2.0 (v5.2.0) 3Copyright (c) 2003-2020 Fabrice Bellard and the QEMU Project developers 1 riscv64-unknown-elf-gcc -T src/asm/linker.ld src/asm/boot.s target/riscv64gc-unknown-none-elf/debug/librust_v_os.a -o rist-v-os -mabi=lp64 -nostdlib 2hattomo@722b63d75c70:~/rust-v-os$ qemu-system-riscv64 -nographic -machine virt -kernel rist-v-os Reference https://risc-v-getting-started-guide.readthedocs.io/en/latest/linux-qemu.html https://github.com/mesonbuild/meson/issues/7258\n","permalink":"https://hattomo.github.io/posts/main/21/q1/0223-rust-v-os/","summary":"\u003ch2 id=\"はじめに\"\u003eはじめに\u003c/h2\u003e\n\u003cp\u003eOSはどのように動いているのか気になり、自作をしてみることにしました。基本の用語から何もわからん\u0026hellip;。\n\u003ccode\u003eRust\u003c/code\u003e+\u003ccode\u003eRISC-V\u003c/code\u003e でいきたい\u0026hellip;\u003c/p\u003e","title":"自作OS 0 環境構築"},{"content":"はじめに WindowsでもmacOS風のキーバインドを利用したいことがあると思います。そのための設定です。レジストリとAutoHotKeyを利用します。\n※レジストリをおかしくいじると最悪Windowsが起動しなくなります。自己責任でお願いします。\nレジストリでキーをリマップする レジストリでキーをリマップするためには、Computer\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Keyboard LayoutにScancode Mapという名称で設定を追加します。空白の場所で右クリックし、New→Binary valueを選択、設定名をScancode Mapとしてください。\n次に、設定を書き込みます。 今回設定するのは以下の項目です。\n1Before → After 2------------------ 3Capslock → F13 4LAlt → LCtrl 5LWin → LAlt 6LCtrl → LWin 7RAlt → RCtrl 8RCtrl → RAlt これをレジストリで設定するためには以下のようにします。\n100000000 00 00 00 00 00 00 00 00 200000008 07 00 00 00 64 00 3A 00 300000010 1D 00 38 00 5B E0 1D 00 400000018 38 00 5B E0 1D E0 38 E0 500000020 38 E0 1D E0 00 00 00 00 600000028 この設定がどのような構造になっているについてはたくさんのブログがあるのでそちらを見てください。\n再起動した際、設定が反映されていれば成功です。\nAutoHotKey を設定する mac風キーバインドの設定 AutoHotKeyをインストールし、xxx.ahkファイルを作成します。\n私のAutoHotKeyのバージョンは1.1.33.02です。\nahkファイルに設定を記述します。\n1;; Eamcs 風のキーバインド 2F13 \u0026amp; B::Send,{Blind}{Left} 3F13 \u0026amp; N::Send,{Blind}{Down} 4F13 \u0026amp; P::Send,{Blind}{Up} 5F13 \u0026amp; F::Send,{Blind}{Right} 6F13 \u0026amp; H::Send,{Blind}{Backspace} 7F13 \u0026amp; D::Send,{Blind}{Delete} 8F13 \u0026amp; A::Send,{Blind}{Home} 9F13 \u0026amp; E::Send,{Blind}{End} 10F13 \u0026amp; K::Send,+{End}{Shift}+{Delete} 11F13 \u0026amp; Enter::Send,{Alt Down}{Shift Down}{Enter}{Alt Up}{Shift Up} 12 13;;バーチャルディスクトップ 14F13 \u0026amp; Right::Send, {LCtrl up}{LWin down}{LCtrl down}{Right}{LWin up}{LCtrl up} 15F13 \u0026amp; Left::Send, {LCtrl up}{LWin down}{LCtrl down}{Left}{LWin up}{LCtrl up} 16F13 \u0026amp; Up::Send, {LWin down}{Tab}{LWin up} 17F13 \u0026amp; Down::Send, {LWin down}{Tab}{LWin up} 18 19;; アプリの終了 20LCtrl \u0026amp; Q::Send, {LAlt down}{F4}{LAlt up} 21 22;;chromeなどのタブの移動 → 23LAlt \u0026amp; Right::\t24 If GetKeyState(\u0026#34;LCtrl\u0026#34;, \u0026#34;P\u0026#34;) {\t25 Send,^{Tab} 26 } 27Return 28 29;;chromeなどのタブの移動 ← 30LAlt \u0026amp; Left::\t31 If GetKeyState(\u0026#34;LCtrl\u0026#34;, \u0026#34;P\u0026#34;) {\t32 Send,+^{Tab} 33 } 34Return 35 36;; アプリ(ウインドウ)の切り替え 37LCtrl \u0026amp; Tab::AltTab 38 39;; 無変換で英語入力 40vk1C:: 41imeoff: 42 Gosub, IMEGetstate 43 If (vimestate=0) { 44 Send, {vkf3} 45 } 46 return 47 48;; 変換で日本語入力 49vk1D:: 50imeon: 51 Gosub, IMEGetstate 52 If (vimestate=1) { 53 Send, {vkf3} 54 } 55 return 56 57;; 上の二つのために必要 58IMEGetstate: 59 WinGet, vcurrentwindow, ID, A 60 vimestate := DllCall(\u0026#34;user32.dll\\SendMessageA\u0026#34;, \u0026#34;UInt\u0026#34;, DllCall(\u0026#34;imm32.dll\\ImmGetDefaultIMEWnd\u0026#34;, \u0026#34;Uint\u0026#34;, vcurrentwindow), \u0026#34;UInt\u0026#34;, 0x0283, \u0026#34;Int\u0026#34;, 0x0005, \u0026#34;Int\u0026#34;, 0) 61 return 62 63;; メディアコントロール(macのファンクションキー) 64;; Insertと数字の同時押しで再現 65;; 数字でなくファンクションにしてもよいのでは? 66;; brightness up 67;;Insert \u0026amp; 1 68;;Insert \u0026amp; 2 69;; task view 70Insert \u0026amp; 3::Send {LWin down}{Tab}{LWin up} 71;; lanch pad 72;;Insert \u0026amp; 4 73;; keyboard brightness up 74;;Insert \u0026amp; 5,6 75;; play Back 76Insert \u0026amp; 7::Send {Media_Prev} 77;; pause \u0026amp; play 78Insert \u0026amp; 8::Send {Media_Play_Pause} 79;; play next 80Insert \u0026amp; 9::Send {Media_Next} 81;; volume mute 82Insert \u0026amp; 0::Send {Volume_Mute} 83;; volume Down 84Insert \u0026amp; -::Send {Volume_Down} 85;; volume up (if en chang e to =) 86Insert \u0026amp; ^::Send {Volume_Up} その他便利な設定 どこかのサイトで見つけました。(忘れてしまいました)\n1;;クリップボード内容をgoogle search 2LAlt \u0026amp; s:: 3\tIf GetKeyState(\u0026#34;Ctrl\u0026#34;, \u0026#34;P\u0026#34;) { 4\tsend, ^c 5\tClipboard := RegExReplace(Clipboard, \u0026#34;^ +|\\r\\n| +$\u0026#34;, \u0026#34;\u0026#34;) 6\tRun, http://www.google.co.jp/search?q=%Clipboard% 7\t} 8Return 9 10;;クリップボード内容をgoogle translate 11LAlt \u0026amp; t:: 12\tIf GetKeyState(\u0026#34;Ctrl\u0026#34;, \u0026#34;P\u0026#34;) { 13\tsend, ^c 14\tClipboard := RegExReplace(Clipboard, \u0026#34;^ +|\\r\\n| +$\u0026#34;, \u0026#34;\u0026#34;) 15\tRun, https://translate.google.com/#view=home\u0026amp;op=translate\u0026amp;sl=en\u0026amp;tl=ja\u0026amp;text=%Clipboard% 16\t} 17Return ","permalink":"https://hattomo.github.io/posts/main/21/q1/0223-autohotkey-mac/","summary":"\u003ch2 id=\"はじめに\"\u003eはじめに\u003c/h2\u003e\n\u003cp\u003eWindowsでもmacOS風のキーバインドを利用したいことがあると思います。そのための設定です。レジストリとAutoHotKeyを利用します。\u003cbr\u003e\n※レジストリをおかしくいじると最悪Windowsが起動しなくなります。自己責任でお願いします。\u003c/p\u003e","title":"Auto HotkeyでMac風キーバインド!"},{"content":"はじめに macOS上では、emacsのキーバインドが一部利用できて便利です。このキーバインドは大抵のテキストエデットアプリには、対応しているのですが、MicrosoftのOffice系アプリでは、これを使うことができません。うっかり慣れで、Ctrl+Hなどを押してしまうと、他の機能が動いてしまいます。\nOfficeでEmacs macOSで、キーバインドのカスタマイズを行おうと思った際にkarabiner-elementsというアプリが有名です。これを使ってOfficeでemacsを利用できるようにしていきます。\n設定は、~/.config/karabiner/assets/complex_modifications/xxxxxx.json(xは数字)にファイルを作成し、以下のように記述します。尚この設定は、Ctrl+H,B,N,P,E,A,Dをサポートしていますが必要に応じて書き換えてください。\n1{ 2\t\u0026#34;title\u0026#34;: \u0026#34;MS-Office de Emacs key\u0026#34;, 3\t\u0026#34;rules\u0026#34;: [ 4\t{ 5\t\u0026#34;description\u0026#34;: \u0026#34;MS-Office de Emacs key\u0026#34;, 6\t\u0026#34;manipulators\u0026#34;: [ 7\t{ 8\t\u0026#34;type\u0026#34;: \u0026#34;basic\u0026#34;, 9\t\u0026#34;from\u0026#34;: { 10\t\u0026#34;key_code\u0026#34;: \u0026#34;b\u0026#34;, 11\t\u0026#34;modifiers\u0026#34;: { 12\t\u0026#34;mandatory\u0026#34;: [ 13\t\u0026#34;control\u0026#34; 14\t] 15\t} 16\t}, 17\t\u0026#34;to\u0026#34;: [ 18\t{ 19\t\u0026#34;key_code\u0026#34;: \u0026#34;left_arrow\u0026#34; 20\t} 21\t], 22\t\u0026#34;conditions\u0026#34;: [ 23\t{ 24\t\u0026#34;type\u0026#34;: \u0026#34;frontmost_application_if\u0026#34;, 25\t\u0026#34;bundle_identifiers\u0026#34;: [ 26\t\u0026#34;^com\\\\.microsoft\\\\.Word$\u0026#34;, 27\t\u0026#34;^com\\\\.microsoft\\\\.Excel$\u0026#34;, 28\t\u0026#34;^com\\\\.microsoft\\\\.Powerpoint$\u0026#34; 29\t] 30\t} 31\t] 32\t}, 33\t{ 34\t\u0026#34;type\u0026#34;: \u0026#34;basic\u0026#34;, 35\t\u0026#34;from\u0026#34;: { 36\t\u0026#34;key_code\u0026#34;: \u0026#34;f\u0026#34;, 37\t\u0026#34;modifiers\u0026#34;: { 38\t\u0026#34;mandatory\u0026#34;: [ 39\t\u0026#34;control\u0026#34; 40\t] 41\t} 42\t}, 43\t\u0026#34;to\u0026#34;: [ 44\t{ 45\t\u0026#34;key_code\u0026#34;: \u0026#34;right_arrow\u0026#34; 46\t} 47\t], 48\t\u0026#34;conditions\u0026#34;: [ 49\t{ 50\t\u0026#34;type\u0026#34;: \u0026#34;frontmost_application_if\u0026#34;, 51\t\u0026#34;bundle_identifiers\u0026#34;: [ 52\t\u0026#34;^com\\\\.microsoft\\\\.Word$\u0026#34;, 53\t\u0026#34;^com\\\\.microsoft\\\\.Excel$\u0026#34;, 54\t\u0026#34;^com\\\\.microsoft\\\\.Powerpoint$\u0026#34; 55\t] 56\t} 57\t] 58\t}, 59\t{ 60\t\u0026#34;type\u0026#34;: \u0026#34;basic\u0026#34;, 61\t\u0026#34;from\u0026#34;: { 62\t\u0026#34;key_code\u0026#34;: \u0026#34;p\u0026#34;, 63\t\u0026#34;modifiers\u0026#34;: { 64\t\u0026#34;mandatory\u0026#34;: [ 65\t\u0026#34;control\u0026#34; 66\t] 67\t} 68\t}, 69\t\u0026#34;to\u0026#34;: [ 70\t{ 71\t\u0026#34;key_code\u0026#34;: \u0026#34;up_arrow\u0026#34; 72\t} 73\t], 74\t\u0026#34;conditions\u0026#34;: [ 75\t{ 76\t\u0026#34;type\u0026#34;: \u0026#34;frontmost_application_if\u0026#34;, 77\t\u0026#34;bundle_identifiers\u0026#34;: [ 78\t\u0026#34;^com\\\\.microsoft\\\\.Word$\u0026#34;, 79\t\u0026#34;^com\\\\.microsoft\\\\.Excel$\u0026#34;, 80\t\u0026#34;^com\\\\.microsoft\\\\.Powerpoint$\u0026#34; 81\t] 82\t} 83\t] 84\t}, 85\t{ 86\t\u0026#34;type\u0026#34;: \u0026#34;basic\u0026#34;, 87\t\u0026#34;from\u0026#34;: { 88\t\u0026#34;key_code\u0026#34;: \u0026#34;n\u0026#34;, 89\t\u0026#34;modifiers\u0026#34;: { 90\t\u0026#34;mandatory\u0026#34;: [ 91\t\u0026#34;control\u0026#34; 92\t] 93\t} 94\t}, 95\t\u0026#34;to\u0026#34;: [ 96\t{ 97\t\u0026#34;key_code\u0026#34;: \u0026#34;down_arrow\u0026#34; 98\t} 99\t], 100\t\u0026#34;conditions\u0026#34;: [ 101\t{ 102\t\u0026#34;type\u0026#34;: \u0026#34;frontmost_application_if\u0026#34;, 103\t\u0026#34;bundle_identifiers\u0026#34;: [ 104\t\u0026#34;^com\\\\.microsoft\\\\.Word$\u0026#34;, 105\t\u0026#34;^com\\\\.microsoft\\\\.Excel$\u0026#34;, 106\t\u0026#34;^com\\\\.microsoft\\\\.Powerpoint$\u0026#34; 107\t] 108\t} 109\t] 110\t}, 111\t{ 112\t\u0026#34;type\u0026#34;: \u0026#34;basic\u0026#34;, 113\t\u0026#34;from\u0026#34;: { 114\t\u0026#34;key_code\u0026#34;: \u0026#34;a\u0026#34;, 115\t\u0026#34;modifiers\u0026#34;: { 116\t\u0026#34;mandatory\u0026#34;: [ 117\t\u0026#34;control\u0026#34; 118\t] 119\t} 120\t}, 121\t\u0026#34;to\u0026#34;: [ 122\t{ 123\t\u0026#34;key_code\u0026#34;: \u0026#34;home\u0026#34; 124\t} 125\t], 126\t\u0026#34;conditions\u0026#34;: [ 127\t{ 128\t\u0026#34;type\u0026#34;: \u0026#34;frontmost_application_if\u0026#34;, 129\t\u0026#34;bundle_identifiers\u0026#34;: [ 130\t\u0026#34;^com\\\\.microsoft\\\\.Word$\u0026#34;, 131\t\u0026#34;^com\\\\.microsoft\\\\.Excel$\u0026#34;, 132\t\u0026#34;^com\\\\.microsoft\\\\.Powerpoint$\u0026#34; 133\t] 134\t} 135\t] 136\t}, 137\t{ 138\t\u0026#34;type\u0026#34;: \u0026#34;basic\u0026#34;, 139\t\u0026#34;from\u0026#34;: { 140\t\u0026#34;key_code\u0026#34;: \u0026#34;e\u0026#34;, 141\t\u0026#34;modifiers\u0026#34;: { 142\t\u0026#34;mandatory\u0026#34;: [ 143\t\u0026#34;control\u0026#34; 144\t] 145\t} 146\t}, 147\t\u0026#34;to\u0026#34;: [ 148\t{ 149\t\u0026#34;key_code\u0026#34;: \u0026#34;end\u0026#34; 150\t} 151\t], 152\t\u0026#34;conditions\u0026#34;: [ 153\t{ 154\t\u0026#34;type\u0026#34;: \u0026#34;frontmost_application_if\u0026#34;, 155\t\u0026#34;bundle_identifiers\u0026#34;: [ 156\t\u0026#34;^com\\\\.microsoft\\\\.Word$\u0026#34;, 157\t\u0026#34;^com\\\\.microsoft\\\\.Excel$\u0026#34;, 158\t\u0026#34;^com\\\\.microsoft\\\\.Powerpoint$\u0026#34; 159\t] 160\t} 161\t] 162\t}, 163\t{ 164\t\u0026#34;type\u0026#34;: \u0026#34;basic\u0026#34;, 165\t\u0026#34;from\u0026#34;: { 166\t\u0026#34;key_code\u0026#34;: \u0026#34;h\u0026#34;, 167\t\u0026#34;modifiers\u0026#34;: { 168\t\u0026#34;mandatory\u0026#34;: [ 169\t\u0026#34;control\u0026#34; 170\t] 171\t} 172\t}, 173\t\u0026#34;to\u0026#34;: [ 174\t{ 175\t\u0026#34;key_code\u0026#34;: \u0026#34;delete_or_backspace\u0026#34; 176\t} 177\t], 178\t\u0026#34;conditions\u0026#34;: [ 179\t{ 180\t\u0026#34;type\u0026#34;: \u0026#34;frontmost_application_if\u0026#34;, 181\t\u0026#34;bundle_identifiers\u0026#34;: [ 182\t\u0026#34;^com\\\\.microsoft\\\\.Word$\u0026#34;, 183\t\u0026#34;^com\\\\.microsoft\\\\.Excel$\u0026#34;, 184\t\u0026#34;^com\\\\.microsoft\\\\.Powerpoint$\u0026#34; 185\t] 186\t} 187\t] 188\t}, 189\t{ 190\t\u0026#34;type\u0026#34;: \u0026#34;basic\u0026#34;, 191\t\u0026#34;from\u0026#34;: { 192\t\u0026#34;key_code\u0026#34;: \u0026#34;d\u0026#34;, 193\t\u0026#34;modifiers\u0026#34;: { 194\t\u0026#34;mandatory\u0026#34;: [ 195\t\u0026#34;control\u0026#34; 196\t] 197\t} 198\t}, 199\t\u0026#34;to\u0026#34;: [ 200\t{ 201\t\u0026#34;key_code\u0026#34;: \u0026#34;delete_forward\u0026#34; 202\t} 203\t], 204\t\u0026#34;conditions\u0026#34;: [ 205\t{ 206\t\u0026#34;type\u0026#34;: \u0026#34;frontmost_application_if\u0026#34;, 207\t\u0026#34;bundle_identifiers\u0026#34;: [ 208\t\u0026#34;^com\\\\.microsoft\\\\.Word$\u0026#34;, 209\t\u0026#34;^com\\\\.microsoft\\\\.Excel$\u0026#34;, 210\t\u0026#34;^com\\\\.microsoft\\\\.Powerpoint$\u0026#34; 211\t] 212\t} 213\t] 214\t} 215\t] 216\t} 217\t] 218} ","permalink":"https://hattomo.github.io/posts/main/21/q1/0215-karabiner-office/","summary":"\u003ch2 id=\"はじめに\"\u003eはじめに\u003c/h2\u003e\n\u003cp\u003emacOS上では、\u003ccode\u003eemacs\u003c/code\u003eのキーバインドが一部利用できて便利です。このキーバインドは大抵のテキストエデットアプリには、対応しているのですが、MicrosoftのOffice系アプリでは、これを使うことができません。うっかり慣れで、\u003ckbd\u003eCtrl\u003c/kbd\u003e+\u003ckbd\u003eH\u003c/kbd\u003eなどを押してしまうと、他の機能が動いてしまいます。\u003c/p\u003e","title":"macOSのOfficeでEmacsキーバインド"},{"content":"はじめに Hugoをつかって、このページを作成していますが、読了時間の表示が常に1 minと表示されていました。おかしいと思っていましたが、さらにrss用のindex.xmlをたまたま見たところ、descriptionタグに記事のほぼすべての文章が入っており、これは日本語が文字数としてカウントされていないためのようでした。\n対処法 config.ymlファイルに\n1HasCJKLanguage: true を追記します。日本語、中国語、韓国語の文字がある場合、これを書いていないと文字カウントがおかしくなってしまうようです。これを追記したところ、正しく動作するようになりました。\n","permalink":"https://hattomo.github.io/posts/main/21/q1/0214-hugo-reading-time/","summary":"\u003ch2 id=\"はじめに\"\u003eはじめに\u003c/h2\u003e\n\u003cp\u003eHugoをつかって、このページを作成していますが、読了時間の表示が常に1 minと表示されていました。おかしいと思っていましたが、さらにrss用のindex.xmlをたまたま見たところ、descriptionタグに記事のほぼすべての文章が入っており、これは日本語が文字数としてカウントされていないためのようでした。\u003c/p\u003e","title":"Hugoで読了時間や文字数表示がおかしい"},{"content":"はじめに 少し前に、ひさしぶりにflutterのイベントFlutter Engageが開かれることが発表されました。コロナウイルスの影響で2020年は、flutterのイベントだけでなく、GoogleIOもなくなってしまい残念でした。\nFlutterの機能管理 Flutterのそれぞれのチャンネルで利用可能なプラットフォームはここで管理されています。Flutterはオープンソースなのでこの場所を見ることでイベントでの発表を予測することができます。\nたとえば、macOSの部分を見ると\n1/// The [Feature] for macOS desktop. 2const Feature flutterMacOSDesktopFeature = Feature( 3 name: \u0026#39;beta-quality support for desktop on macOS\u0026#39;, 4 configSetting: \u0026#39;enable-macos-desktop\u0026#39;, 5 environmentOverride: \u0026#39;FLUTTER_MACOS\u0026#39;, 6 extraHelpText: flutterNext ? 7 \u0026#39;Newer beta versions are available on the beta channel.\u0026#39; : null, 8 master: FeatureChannelSetting( 9 available: true, 10 enabledByDefault: false, 11 ), 12 dev: FeatureChannelSetting( 13 available: true, 14 enabledByDefault: false, 15 ), 16 beta: FeatureChannelSetting( 17 available: flutterNext, 18 enabledByDefault: false, 19 ), 20 stable: FeatureChannelSetting( 21 available: flutterNext, 22 enabledByDefault: false, 23 ), 24); このようになっています。availableの部分にflutterNextと書かれていますが、ファイルの最後の行に\n1const bool flutterNext = true; このように定義されています。この変更は最近なされたものであり、次のイベントでβ版に昇格するということだと考えられます。同じように変更はWindowsとLinux向けにもなされています。Web版はstableリリースとなるようです。\nこのほかにも、The fast hot reload feature(singleWidgetReload)やThe CFE experimental invalidation strategy(なんだろう?)などが開発されているようです。(2021/02/14 現在)\n気になるのは残るfuchsiaです。Androidを置き換えるのではないかといううわさが出ていますが\u0026hellip;。Flutterでは、masterのみで利用できるように設定されています。\n","permalink":"https://hattomo.github.io/posts/main/21/q1/0214-flutter-chaneel/","summary":"\u003ch2 id=\"はじめに\"\u003eはじめに\u003c/h2\u003e\n\u003cp\u003e少し前に、ひさしぶりに\u003ccode\u003eflutter\u003c/code\u003eのイベント\u003ca href=\"https://events.flutter.dev/\"\u003eFlutter Engage\u003c/a\u003eが開かれることが発表されました。コロナウイルスの影響で2020年は、flutterのイベントだけでなく、GoogleIOもなくなってしまい残念でした。\u003c/p\u003e","title":"Flutterが使える機能の管理"},{"content":"はじめに WindowsをLinuxやmacOS(bootcampでない)とデュアルブートしていると、OSの時計表示がおかしくなってしまうことがあります。\nどうしておかしくなるのか Windowsは内部でローカルの時間を利用しています。日本であればUTC+09:00です。電源を切るとき、BIOSにこの値を保存します。これをハードウェアクロック、RTC、CMOSクロックと呼びます。BIOSは搭載された電池によって、この値を保持・更新し、次回Windowsが起動する際にWindowsに渡します。しかし、LinuxやmacOSでは、UTCそのものでハードウェアクロックを管理し、表示する際にはタイムゾーンに合わせた値を計算します。このため、例えば、WindowsがUTC+09:00としてシャットダウン時に保存した値をLinuxはUTCと解釈してしまうのです。逆もまた然りです。このため、どちらかの方法に統一する必要があります。今回はWindowsのハードウェアクロックをUTCにあわせます。\nWindowsをUTCに コマンドプロンプトを開いて以下のコマンドを実行します。このコマンドはレジストリを変更し、WindowsのハードウェアクロックをUTCに合わせます。コマンドを実行する際管理者権限が必要です。\n1# set UTC 2reg add \u0026#34;HKEY_LOCAL_MACHINE\\System\\CurrentControlSet\\Control\\TimeZoneInformation\u0026#34; /v RealTimeIsUniversal /d 1 /t REG_DWORD /f 戻す際は、下のコマンドを実行してください。\n1# unset 2reg delete HKLM\\SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation /v RealTimeIsUniversal /f このあと時刻表示がおかしくなっているOSをNTPサーバーと同期して正しい時刻に修正すれば、完了です。\n","permalink":"https://hattomo.github.io/posts/main/21/q1/0213-mangae-hardware-clock-windows-utc/","summary":"\u003ch2 id=\"はじめに\"\u003eはじめに\u003c/h2\u003e\n\u003cp\u003eWindowsをLinuxやmacOS(bootcampでない)とデュアルブートしていると、OSの時計表示がおかしくなってしまうことがあります。\u003c/p\u003e","title":"WindowsのハードウェアクロックをUTCで管理する"},{"content":"はじめに Hugoで新しいポストを作成するコマンドは、\n1hugo new [path to new file] ですが、私はフォルダを分けているので\n1hugo new posts/21/Q1/0213-[title] のような長いパスになっていました。いちいち入力するのはめんどくさいし、よく間違えるのでシェルスクリプトを作りました。\n1# usage 2./new.sh title 作り方 中身は以下のようになっています。\n1title=$1 2year=`date \u0026#39;+%y\u0026#39;` 3quoter=`date \u0026#39;+Q%q\u0026#39;` 4date=`date \u0026#39;+%m%d-\u0026#39;` 5path=posts/$year/$quoter/$date$title/index.md 6hugo new $path まず、$1はコマンドライン引数を表しています。ここにタイトルが入ります。\nまた、このようにすると変数varにコマンドの結果を入れることができます。\n1var=`command` dateコマンドを利用して必要な値を取得し、変数に入れ、$pathで結合しています。 あとはコマンドを実行して新しいポストを生成するだけです。 と思いましたが、macOSで動作しません。macOSのdateコマンドには、%qがなくクオータが取得できません。そこで、if-elif-elseを使って書き直しました。\n1# useage ./newpost title 2# $1 := titile 3 4title=$1 5year=`date \u0026#39;+%y\u0026#39;` 6month=`date \u0026#39;+%m\u0026#39;` 7date=`date \u0026#39;+%d-\u0026#39;` 8#quoter=`date \u0026#39;+Q%q\u0026#39;` # for Linux, not for macOS 9if [ $month == 01 ] || [ $month == 02 ] || [ $month == 03 ]; then 10 quoter=1 11elif [ $month == 04 ] || [ $month == 05 ] || [ $month == 06 ]; then 12 quoter=2 13elif [ $month == 07 ] || [ $month == 08 ] || [ $month == 09 ]; then 14 quoter=3 15else 16 quoter=4 17fi 18path=posts/$year/Q$quoter/$month$date$title/index.md 19hugo new $path これで目的を達成できました。\n","permalink":"https://hattomo.github.io/posts/main/21/q1/0213-new-post/","summary":"\u003ch2 id=\"はじめに\"\u003eはじめに\u003c/h2\u003e\n\u003cp\u003eHugoで新しいポストを作成するコマンドは、\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-sh\" data-lang=\"sh\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"ln\"\u003e1\u003c/span\u003e\u003cspan class=\"cl\"\u003ehugo new \u003cspan class=\"o\"\u003e[\u003c/span\u003epath to new file\u003cspan class=\"o\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eですが、私はフォルダを分けているので\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-sh\" data-lang=\"sh\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"ln\"\u003e1\u003c/span\u003e\u003cspan class=\"cl\"\u003ehugo new posts/21/Q1/0213-\u003cspan class=\"o\"\u003e[\u003c/span\u003etitle\u003cspan class=\"o\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eのような長いパスになっていました。いちいち入力するのはめんどくさいし、よく間違えるのでシェルスクリプトを作りました。\u003c/p\u003e","title":"シェルでHugoのポストを新規作成する"},{"content":"はじめに GitHub Codespacesがβになってからしばらくたちました。βの間は無料のようなので、気軽に試すことができます。今回はGithub CodespacesでHugoを使って記事をかいてみます。(この記事はGitHubCodespaces上で書いています。)\nHugoで使う GitHub CodespacesでHugoを使う場合、デフォルトのコンテナでもHugoを利用することができますが、Hugoのイメージが公開されていますのでそれを利用するとよいように思います。 コンテナの変更は、コマンドパレットに、\n1Codespaces : Add Development Container Configuration Files ... と入力し、Hugoを選択し、コンテナをリビルドすることで可能です。 イメージは、Github microsoft/vscode-dev-containersで公開されています。 これに限らず、Codespacesの環境は.devcontainerフォルダを作り中に設定を記述することで設定できます。\nHugoのサーバーを起動するコマンド\n1hugo server -D を実行し、localhost:1313にアクセスすれば、プレビューを見ることができます。\n感想 リモートにつないでいるにも関わらず、かなり快適に作業することができます。さらにVSCodeがエクステンションやテーマ、キーバインドも含めて完全に動いているのでリモートに接続していることを忘れてしまいそうです。\nリモートマシンは、OS:Ubuntu18.04、CPU:Intel(R) Xeon(R) Platinum 8168 CPU @ 2.70GH、RAM 8GB、Stroge:32GBで動いているようです。Hugoを使うだけなら十分すぎます。\nWebブラウザでコードを書くのは、最近のトレンドになっていますが、GoogleのStdiaとか、Microsoft WindowsのCloud PCとか、クラウド上のコンピューターで作業を行うことが当たり前になっていくのでしょうね。 セルフホストはこれを書いている現在はできないようですが、個人向けのCodespacesは当面無料であることが発表されたため、重い作業でないこのようなタスクでは必要なさそうです。\n","permalink":"https://hattomo.github.io/posts/main/21/q1/0213-how-to-use-hugo-in-github-codespaces/","summary":"\u003ch2 id=\"はじめに\"\u003eはじめに\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003eGitHub Codespaces\u003c/code\u003eがβになってからしばらくたちました。βの間は無料のようなので、気軽に試すことができます。今回はGithub CodespacesでHugoを使って記事をかいてみます。(この記事はGitHubCodespaces上で書いています。)\u003c/p\u003e","title":"GitHub CodespacesでHugoを利用したブログを書く"},{"content":"はじめに Hugoでは、記事の最終更新時刻をLastmodを利用して表すことができます。configファイルでenableGitInfo: trueと記入しておくとgitのlogをもとにHugoが自動的にLastmodを設定しくれます。しかし、GitHub ActionsでHugoをビルドしたところ、すべてのLastmodが同じ時間(pushした時刻)になってしまっていました。\n解決法 GitHub Actionsのファイルは以下のようでした。\n1name: Build GH-Pages 2 3on: 4 push: 5 branches: 6 - main 7 8jobs: 9 deploy: 10 runs-on: macos-latest 11 steps: 12 - name: Git checkout 13 uses: actions/checkout@v2 14 with: 15 submodules: recursive # Fetch Hugo themes (true OR recursive) 16 17 - name: Setup hugo 18 uses: peaceiris/actions-hugo@v2 19 with: 20 hugo-version: \u0026#39;latest\u0026#39; 21 22 - name: Build 23 run: hugo --gc --verbose --minify 24 25 - name: Deploy 26 uses: peaceiris/actions-gh-pages@v3 27 with: 28 deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }} 29 external_repository: Hattomo/Hattomo.github.io 30 publish_branch: main 31 publish_dir: ./public 問題は、ソースをダウンロードする際、fetch-depthがデフォルトで1になっていることでした。fetch-depthが1の場合、最新のコードのみを持ってくるようです。そのため、履歴がなくLastmodが同一時刻になっていたのでした。以下のように、fetch-depthに0を設定したところ正しく動くようになりました。\n1 steps: 2 - name: Git checkout 3 uses: actions/checkout@v2 4 with: 5 submodules: recursive 6 fetch-depth: 0 # Add ","permalink":"https://hattomo.github.io/posts/main/21/q1/0213-github-actions-hugo-lastmod/","summary":"\u003ch2 id=\"はじめに\"\u003eはじめに\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003eHugo\u003c/code\u003eでは、記事の最終更新時刻を\u003ccode\u003eLastmod\u003c/code\u003eを利用して表すことができます。configファイルで\u003ccode\u003eenableGitInfo: true\u003c/code\u003eと記入しておくとgitのlogをもとにHugoが自動的にLastmodを設定しくれます。しかし、GitHub ActionsでHugoをビルドしたところ、すべてのLastmodが同じ時間(pushした時刻)になってしまっていました。\u003c/p\u003e","title":"HugoでLastmodが同一時刻になる"},{"content":"はじめに 様々なサイトに導入されているGoogle Analyticsですが、2020年より新たにGoogle Analytics 4(以下GA4)が、導入されました。しかしHugoでは、標準ではまだ対応していません。(これを書いているときの最新バージョンは0.80です)しかし、HugoではGA4を簡単に利用することができます。\nGA4を導入する GoogleAnalytics にアクセスし、GA4のIDを取得します。その方法はここでは省略します。GA4のIDはG-xxxxxxxxxxのようにGから始まります。UAから始まっている場合は、従来のIDです。\nIDが取得出来たら、Hugoのフォルダtheme/layoutの適当なところに新規HTMLファイルを作成し、以下のように追記します。\nanalytics-gtag.html 1\u0026lt;!-- Global site tag (gtag.js) - Google Analytics 4--\u0026gt; 2\u0026lt;script async src=\u0026#34;https://www.googletagmanager.com/gtag/js?id={{ .Site.GoogleAnalytics }}\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; 3\u0026lt;script\u0026gt; 4 window.dataLayer = window.dataLayer || []; 5 function gtag() { dataLayer.push(arguments); } 6 gtag(\u0026#39;js\u0026#39;, new Date()); 7 8 gtag(\u0026#39;config\u0026#39;, \u0026#39;{{ .Site.GoogleAnalytics }}\u0026#39;); 9\u0026lt;/script\u0026gt; 次に、configファイルに移動し、GoogleAnalyticsのIDを設定します。ymlの場合は、以下のようになります。\n1GoogleAnalytics: G-xxxxxxxx 最後にこれらの設定を読み込みます。theme/layouts/partials/head.htmlのファイルの一番下のほうにある外部ファイルの読み込みを修正します。google_analytics_asyncは従来のgoogle analyticsなので消します。逆に、先ほど作成したファイルのパスを下のように追記します。\n1~省略~ 2{{- template \u0026#34;_internal/google_analytics_async.html\u0026#34; . }} \u0026lt;!--Delete--\u0026gt; 3{{- template \u0026#34;{path to file}/analytics-gtag.html\u0026#34; . }} \u0026lt;!--Add GA4--\u0026gt; 4{{- template \u0026#34;_internal/google_news.html\u0026#34; . }} 5{{- template \u0026#34;partials/templates/opengraph.html\u0026#34; . }} 6{{- template \u0026#34;partials/templates/twitter_cards.html\u0026#34; . }} 7{{- template \u0026#34;partials/templates/schema_json.html\u0026#34; . }} 8~省略~ 以上で完了です。\n適当なページを作り、アクセスした状態で、Google Analyticsのリアルタイムを確認して下さい。ユーザーが確認できれば、成功です。\n","permalink":"https://hattomo.github.io/posts/main/21/q1/0213-new-google-analytics-4/","summary":"\u003ch2 id=\"はじめに\"\u003eはじめに\u003c/h2\u003e\n\u003cp\u003e様々なサイトに導入されているGoogle Analyticsですが、2020年より新たにGoogle Analytics 4(以下GA4)が、導入されました。しかし\u003ccode\u003eHugo\u003c/code\u003eでは、標準ではまだ対応していません。(これを書いているときの最新バージョンは0.80です)しかし、\u003ccode\u003eHugo\u003c/code\u003eではGA4を簡単に利用することができます。\u003c/p\u003e","title":"HugoでGoogle Analytics 4を利用する"},{"content":"はじめに MicrosoftのVSCodeでデフォルトのフォントを確認する方法です。先日、このサイトのほかのページにコードを書いたところ、macOSではきれいに表示されていましたが、Windowsでは汚いフォントで表示されていました。もちろんCSSをいじってFont-Familyを設定すればいいわけですが、どれを設定すればいいかわからない!ってことでVSCodeのデフォルトのフォントと同じフォントを設定すればきれいではないかと思い調べました。\n方法 シンプルにソースコードを見に行くのが早いでしょう(たぶん)。ソースコードは、GitHubのmicrosoft/vscodeで公開されており、そのなかでフォントを指定している部分はここです。27~29行目を見ると、以下のような記述があります。\n1.mac { --monaco-monospace-font: \u0026#34;SF Mono\u0026#34;, Monaco, Menlo, Courier, monospace; } 2.windows { --monaco-monospace-font: Consolas, \u0026#34;Courier New\u0026#34;, monospace; } 3.linux { --monaco-monospace-font: \u0026#34;Ubuntu Mono\u0026#34;, \u0026#34;Liberation Mono\u0026#34;, \u0026#34;DejaVu Sans Mono\u0026#34;, \u0026#34;Courier New\u0026#34;, monospace; } どうやら、OSによって異なるフォントを使っているようです。macOSではSF Mono、WindowsではConsolas、LinuxではUbuntu Monoのようです。Linuxの最初がUbuntu Monoなので、LinuxでVSCodeを利用する人は、Ubuntuが一番多そうです。このサイトのCSSにも、これらのフォントを指定しておきました。\n","permalink":"https://hattomo.github.io/posts/main/21/q1/0213-vscode-default-font/","summary":"\u003ch2 id=\"はじめに\"\u003eはじめに\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003eMicrosoft\u003c/code\u003eの\u003ccode\u003eVSCode\u003c/code\u003eでデフォルトのフォントを確認する方法です。先日、このサイトのほかのページにコードを書いたところ、macOSではきれいに表示されていましたが、Windowsでは汚いフォントで表示されていました。もちろんCSSをいじってFont-Familyを設定すればいいわけですが、どれを設定すればいいかわからない!ってことで\u003ccode\u003eVSCode\u003c/code\u003eのデフォルトのフォントと同じフォントを設定すればきれいではないかと思い調べました。\u003c/p\u003e","title":"VSCodeのデフォルトフォントを確認する方法"},{"content":"アクセス解析ツールについて 当サイトでは、Googleによるアクセス解析ツール「Googleアナリティクス」を利用しています。\nこのGoogleアナリティクスはトラフィックデータの収集のためにCookieを使用しています。このトラフィックデータは匿名で収集されており、個人を特定するものではありません。この機能はCookieを無効にすることで収集を拒否することが出来ますので、お使いのブラウザの設定をご確認ください。この規約に関して、詳しくはこちらをクリックしてください。\n免責事項 当サイトで掲載している画像の著作権・肖像権等は各権利所有者に帰属致します。権利を侵害する目的ではございません。記事の内容や掲載画像等に問題がございましたら、各権利所有者様本人が直接ご連絡下さい。確認後、対応させて頂きます。\n当サイトからリンクやバナーなどによって他のサイトに移動された場合、移動先サイトで提供される情報、サービス等について一切の責任を負いません。\n当サイトのコンテンツ・情報につきまして、可能な限り正確な情報を掲載するよう努めておりますが、誤情報が入り込んだり、情報が古くなっていることもございます。\n当サイトに掲載された内容によって生じた損害等の一切の責任を負いかねますのでご了承ください。\n","permalink":"https://hattomo.github.io/general/privacypolicy/","summary":"\u003ch2 id=\"アクセス解析ツールについて\"\u003eアクセス解析ツールについて\u003c/h2\u003e\n\u003cp\u003e当サイトでは、Googleによるアクセス解析ツール「Googleアナリティクス」を利用しています。\u003c/p\u003e\n\u003cp\u003eこのGoogleアナリティクスはトラフィックデータの収集のためにCookieを使用しています。このトラフィックデータは匿名で収集されており、個人を特定するものではありません。この機能はCookieを無効にすることで収集を拒否することが出来ますので、お使いのブラウザの設定をご確認ください。この規約に関して、詳しくは\u003ca href=\"https://marketingplatform.google.com/about/analytics/terms/jp/\"\u003eこちら\u003c/a\u003eをクリックしてください。\u003c/p\u003e","title":"Privacy Policy"},{"content":"Hugo をインストールする homebrewを利用した方法が、一般的なようでしたが、Install hugoを参考にGitHubからバイナリをダウンロードし、解凍したhugoの実行ファイルを/usr/local/binに配置しました。\nテーマを決める テーマは少し迷いましたが、hugo-PaperModにしました。開発が活発に続けられていたこと、ドキュメントが整備されていたこと、デザインが気に入ったためです。\ngitのサブモジュールに登録します。\ngit submodule add https://github.com/adityatelange/hugo-PaperMod.git themes/PaperMod --depth=1 git submodule update --init --recursive 今後テーマをアップデートするためには、以下のコマンドを実行します。\n1git submodule update --remote --merge 新規記事を作成する 以下のコマンドを実行します。\n1hugo new posts/{path to new file}.md Hugo ローカルサーバーを立ち上げる 1hugo server -D テーマを編集する テーマをフォークし、次のような変更を行いました(行う予定です)。\n文字サイズの変更 google analytics の追加 klatexのサポート Syntax Highlightingの設定 前回の記事、次の記事へのリンクの追加 GitHubにpushしたらdeployが行われるよう設定する GitHub actionを利用して、自動的にgithub-pagesにdeployが行われるように設定します。 peaceiris/actions-hugo@v2とpeaceiris/actions-gh-pages@v3を利用しました。\nReference https://gohugo.io/getting-started/quick-start/ https://github.com/adityatelange/hugo-PaperMod ","permalink":"https://hattomo.github.io/posts/main/21/q1/0127-how-to-use-hugo/","summary":"\u003ch2 id=\"hugo-をインストールする\"\u003eHugo をインストールする\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003ehomebrew\u003c/code\u003eを利用した方法が、一般的なようでしたが、\u003ca href=\"https://gohugo.io/getting-started/installing#binary-cross-platform\"\u003eInstall hugo\u003c/a\u003eを参考にGitHubからバイナリをダウンロードし、解凍したhugoの実行ファイルを\u003ccode\u003e/usr/local/bin\u003c/code\u003eに配置しました。\u003c/p\u003e","title":"How to install and use Hugo"},{"content":"About Me I am a master student at University in 🗾(Japan). I ❤️ computer science. I research to improve the accuracy of speech recognition for minority languages.\nSocial links are here👋\n#Python, #Flutter, #VSCode, #Machine #Learning.\nComputer Skills Python🐍 Machine Learning Flutter(contributor) \u0026amp; Dart and much more!\nPublications Hattori, T. and Tamura, S. (2023). Speech Recognition for Minority Languages Using HuBERT and Model Adaptation. In Proceedings of the 12th International Conference on Pattern Recognition Applications and Methods, ISBN 978-989-758-626-2, ISSN 2184-4313, pages 350-355. DOI: 10.5220/0011682700003411 My Computers💻 Computers which I use.\nMacbook Pro 13inch 2017\nParts Model OS macOS CPU Intel Corei5 7360U Memory 8GB 2133 MHz SSD 256GB Self build PC (Family sharing)\nParts Model OS Windows 11 CPU Intel Corei5 8400 Memory 16GB 2666 MHz SSD 500GB Raspberry Pi 3B+\nParts Model OS Raspbian SoC Broadcom BCM2837B0 Memory 1GB Micro SD Card 16GB Lab PC (Desktop)\nParts Model OS Ubuntu 22.04 CPU Intel Corei5 8700k GPU Geforce RTX 3090 + Geforce RTX 2080TI Memory 64GB 2133 MHz SSD 500GB HDD 4TB Surface Pro 7+ (Lab PC (Laptop))\nParts Model OS Windows 11 CPU Intel Corei5 1135G7 Memory 16GB 4267 MHz SSD 256GB ","permalink":"https://hattomo.github.io/general/about/","summary":"\u003ch2 id=\"about-me\"\u003eAbout Me\u003c/h2\u003e\n\u003cp\u003eI am a master student at University in 🗾(Japan). I ❤️ computer science. I research to improve the accuracy of speech recognition for minority languages.\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"/\"\u003eSocial links\u003c/a\u003e are here👋\u003c/p\u003e\n\u003cp\u003e#Python, #Flutter, #VSCode, #Machine #Learning.\u003c/p\u003e\n\u003ch3 id=\"computer-skills\"\u003eComputer Skills\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003ePython🐍\u003c/li\u003e\n\u003cli\u003eMachine Learning\u003c/li\u003e\n\u003cli\u003eFlutter(contributor) \u0026amp; Dart\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eand much more!\u003c/p\u003e\n\u003ch3 id=\"publications\"\u003ePublications\u003c/h3\u003e\n\u003c!-- Harvard Citation Format --\u003e\n\u003cul\u003e\n\u003cli\u003eHattori, T. and Tamura, S. (2023). Speech Recognition for Minority Languages Using HuBERT and Model Adaptation. In Proceedings of the 12th International Conference on Pattern Recognition Applications and Methods, ISBN 978-989-758-626-2, ISSN 2184-4313, pages 350-355. DOI: 10.5220/0011682700003411\u003c/li\u003e\n\u003c/ul\u003e\n\u003c!-- ### talks --\u003e\n\u003ch3 id=\"my-computers\"\u003eMy Computers💻\u003c/h3\u003e\n\u003cp\u003eComputers which I use.\u003c/p\u003e","title":"About"}]