はじめに
- 文章を書く時に改行位置をどこで区切るかは難しい。画面右端に達したら折り返す仕組みは様々な言語でサポートされているが、中途半端な所で改行されると読みにくい
- Googleから読みやすい改行を実現する、BudouXと呼ばれるモデルが開発された
- 本ページでは、BudouXの概略とFlutterで使用してみた結果を残す
BudouX
BudouXとは
- Google JPが開発した、読みやすい改行のためオープンソースの分かち書き器
- 日本語、中国語、タイ語をJava, Python, JavaScriptでサポート
- 特徴
- スタンドアロンで動作
- 20KB程度に収まる軽量モデル
- 言語に依存しない(学習させることができる)
モデルの特徴
BudouXは、ブースティング(弱学習器の繰り返しによる学習)を使ったAdaboostでモデル構築されており、日本語モデルのJsonの一部を下記に示す
{
"UW3": {
"。": 6699,
"、": 4784,
"を": 5769,
"っ": -1853
...
},
"UW1": {
"お": 922,
"ご": 890,
"ふ": 1899,
"甘": -1424
...
},
"BW1": {
"のみ": 3058,
"以上": 1223,
"まま": 2830,
"ちが": -2703
...
},
"BW3": {
"うま": 4971,
"もの": 3479,
"あり": 1053,
"おり": -2875,
...
},
"TW2": {
"気に入": -4086,
"ではな": -1086,
"とがあ": -1130,
"そもそ": -1441,
"してい": -406
},
"TW1": {
"という": 545,
"ていく": 1686,
"ような": 1298,
"しかし": 2079,
"持って": -1851
}
...
}
- UW3, BW1, ... といった各カテゴリに対して、キーバリューが定義
- UW (Unigram Wieght):特定の位置にある文字の重み
- BW (Bigram Weight):隣接する2つの文字の組み合わせの重み
- TW (Trigram Weight):隣接する3つの文字の組み合わせの重み
- つまり、「何の文字が来たら改行するだろう」と、文字の種類ごとに重要度を割り当てているシンプルなモデル
- 今回利用したBudouXモデルの処理コードを見ると、UX, BW, TWのスコアを算出し、スコアが0以上のときに改行している
Flutterのサンプルサンプルコード
日本語で書かれた文字列を読み込み、表示するサンプルを以下に示す。
なお実装には、BudouXをFlutterに移植されていたbudoux_dartを利用した。
import 'package:budoux_dart/budoux.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
runApp(const MainApp());
}
class MainApp extends StatelessWidget {
const MainApp({super.key});
// BudouXモデルを非同期でロード
Future<BudouX> loadBudouxModel() async {
final modelJson = await rootBundle.loadString('assets/ja.json');
return BudouX(modelJson);
}
// テキスト
final simpleText = '今日は全国的に晴れです。洗濯物がすぐに乾きます。ですが、'
'明日は1日中曇りになる見込みです。傘の準備をしておいた方が良いでしょう。'
'さらに、明後日は雨です。';
final complexText = 'Flutter(フラッター)は、Googleによって開発されたフリーかつ'
'オープンソースのUIのSDKである。単一のコードベースから、Android、iOS、Linux、macOS、'
'Windows、Google Fuchsia向けのクロスプラットフォームアプリケーションを'
'開発するために利用される。'
'2018年12月4日、ロンドンで開催されたFlutter Live 18にて、初の正式版となる'
'Flutter 1.0のリリースが発表された。';
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: FutureBuilder<BudouX>(
future: loadBudouxModel(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) {
return const Center(child: Text('Error loading model'));
// コンテンツの表示
} else {
return buildContent(snapshot.data!);
}
},
)),
);
}
Widget buildContent(BudouX budouX) {
final parsedSimpleText = parseText(budouX, simpleText);
final parsedComplexText = parseText(budouX, complexText);
return Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
// simpleTextの改行なし文章 & BudouX適用した文章
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(width: 20),
buildContainer(Text(simpleText)),
const SizedBox(width: 20),
buildContainer(Wrap(children: parsedSimpleText)),
],
),
// complexTextの改行なし文章 & BudouX適用した文章
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(width: 20),
buildContainer(Text(complexText)),
const SizedBox(width: 20),
buildContainer(Wrap(children: parsedComplexText)),
],
)
],
);
}
Widget buildContainer(Widget child) {
return Container(
decoration: BoxDecoration(
border: Border.all(),
),
width: 200,
child: child,
);
}
// テキストを解析してウィジェットのリストを返すメソッド
List<Widget> parseText(BudouX budouX, String text) {
// BudouXでは、改行の単位でリストに格納されている
return budouX.parse(text).map((rowText) => Text(rowText)).toList();
}
}
...
dependencies:
flutter:
sdk: flutter
budoux_dart: ^1.0.2
...
flutter:
uses-material-design: true
assets:
- assets/ja.json
実行結果
- 左側:原文、右側:BudouXで加工した文
- 単純な平仮名・漢字で構成された文章は、読みやすく改行されている
- 一方、英語が混じった文章はモデルに組み込まれていないため、読みやすいとまでは言えない(多分、カタカナも?)
まとめ
- BudouXは、軽量なモデルで利用できる改行
- シンプルな文章では、その強みが発揮されていそう
- 一方で、英語やカタカナが混じる文章では、中途半端な位置で改行されていた
- 安易にUIに組み込むよりも、使いどころが大事そう