SAM【1】:何でもセグメント化


序文

Segment Anthing は、Meta の最初のオープンソースのセグメント化された大規模モデルであり、最近 CV 分野で大規模モデルの波を引き起こしています。わずか数日の間に、さまざまな二次的なイノベーションや評価が次々と生まれました。同時に、Meta はモデルのデモSAMをリリースし、研究者が の魔法と力を体験できるようにしました。

ビジュアルラージモデルは自然言語ラージモデルと似ており、その主な目的はユーザーのすべての問題を1つのモデルで解決することです。SAM は、画像データの幅広い画像タイプとタスクによって制限されているため、現在は主に最も伝統的で広く使用されているセグメンテーション タスクを解決します。SAMNLP プロンプト パラダイムを CV 分野に導入することにより、CV 基本モデルに対するより広範なサポートと詳細な調査が提供されます。適切なプロンプトを構築することで、新しいサンプルをゼロショットする機能が実現され、場合によってはモデルがタスクは設計時に考慮されていません。

この記事は主に SAM の方法を分析し、その後の大規模モデルの研究のための良い基礎も築きます。SAMモデルのアーキテクチャとメソッドだけを理解したい場合は、この記事のセクション 2.2 を直接読むことができます。

元の論文リンク: Segment Anything


1. 抽象化と導入

1.1. 抽象化

この論文では、画像セグメンテーションのための新しいタスク、モデル、データセットを提案します。モデルの設計とトレーニングは柔軟であるため、ゼロショット (ゼロショット) を新しい画像ディストリビューションとタスクに転送できます。実験では、多くのタスクでその機能を評価し、ゼロショットのパフォーマンスが印象的であることがわかり、多くの場合、完全に監視された以前の結果よりも優れているか、それよりも優れています。

1.2. 導入

ネットワーク データセットで事前トレーニングされた大規模な言語モデルには、強力なゼロショットおよび少数ショットの一般化機能があります。これらの基本モデルは、トレーニング プロセスを超えてタスクやデータ分布に拡張できます。この機能はprompt engineering

CLIPこの基本モデルは、対比学習を使用してテキストと画像のエンコーディングを調整したり、プロンプトを通じて画像エンコーダーを生成したりするなど、視覚タスクでも検討されておりALIGN、画像の生成などの下流タスクに拡張できます。

この研究の目的は、プロンプト可能なモデルを開発することです。このモデルは、特定のタスクを通じて大規模なデータセットで事前トレーニングされており、強力な一般化が可能です。つまり、一連の下流のセグメンテーションタスクを解決するためのプロンプト (プロンプト) が可能です。新しいデータセットについて

上記の目標を達成するために、この文書では解決する必要がある 3 つの問題を提案します。

  • どのようなタスクをzero-shot一般化できますか?
  • 対応するネットワーク構造は何ですか?
  • このようなタスクやモデルを推進できるのはどのようなデータセットでしょうか?

ここに画像の説明を挿入

要約すると、このペーパーでは次の解決策を提案し、その他の関連する問題についても検討します。

  • タスク
    • プロンプト可能なセグメンテーション タスクを作成して、pointboxMasktext (未実装) などのあらゆる形式のセグメンテーション プロンプトに対して、効果的なセグメンテーション マスクを返すことができるようにします。
    • 入力プロンプトがあいまいな場合でも、モデルはより合理的なセグメンテーション結果を出力できます。
    • prompt engineering
      • ヒント エンジニアリングとは、特定の下流セグメンテーション タスクの解決に役立つヒントを設計するプロセスを指します。
      • ヒント可能なセグメンテーション タスクの事前トレーニングで得られた知識を使用することで、画像内の特定のオブジェクトまたは領域に対して効果的なセグメンテーション マスクを生成するようにモデルをガイドするヒントを設計できます。
  • モデル
    • タスク要件から始めて、モデルは次の 3 つの点を満たす必要があります。
      • 柔軟なプロンプト情報をサポートできる
      • インタラクティブな使用のためにリアルタイムでマスクを計算する機能
      • 曖昧な感覚を持ってください。
    • この論文では、上記の 3 つの要件を満たすモデル アーキテクチャを提案しています。モデルは柔軟なプロンプトをサポートし、対話的に生成されたマスクをリアルタイムで計算できる必要があるため、著者は画像エンコーダと高速プロンプト エンコーダを設計し、軽量のプロンプト エンコーダを渡しましプロンプト エンコーダーは出力セグメンテーション マスクを結合して予測します。
  • データエンジンとデータセット
    • 強力な一般化モデルには、多様性に富んだ大規模なデータ セットが必要です。この論文では、イメージ マスクの不足を補うデータ エンジンを構築しており、これは 3 つのステップに分かれています。
      • 人間による支援 (インタラクティブなセグメンテーションと同様のヘルプのラベル付け)
      • 半自動 (ヒントを提供することでオブジェクト マスクを自動的に生成します)
      • 完全自動 (通常のグリッドをプロンプトとして使用して自動的に生成)
    • 新しく構築されたデータセットSA-1B には、既存のデータセットの 400 倍以上のサイズである 1,100 万以上の画像と 10 億のマスクが含まれています (オープンアクセス)

2. 何でもモデルをセグメント化する

2.1. 何でもタスクをセグメント化する

2.1.1. タスク

prompt前景/背景点のセット、大まかなボックスまたはマスク、自由形式のテキスト(画像内でセグメント化する内容を示す任意の情報) を指定でき、プロンプトに従って有効なセグメンテーション マスクを返します。効率的とは、ユーザーのpromptセグメンテーション マスクがあいまいであっても、モデルがユーザーが選択できる複数の妥当なセグメンテーション マスクを出力できることを意味します。

このタスクは、自然な事前トレーニング アルゴリズムと、ヒントを介して下流のセグメンテーション タスクにゼロショット転送するための一般的な方法につながります。

ここに画像の説明を挿入

2.1.2. 事前トレーニング

この論文はインタラクティブ セグメンテーションからインスピレーションを得ています。

インタラクティブ セグメンテーション: インタラクティブ セグメンテーションは、ユーザー入力に基づいて画像をさまざまな領域またはオブジェクトにセグメント化するようにアルゴリズムがトレーニングされる典型的なコンピューター ビジョン タスクを指します。これは、アルゴリズムがユーザーからヒントや手がかりを得て、セグメンテーションの結果を調整できることを意味します。言い換えれば、ユーザーはアルゴリズムを操作して、より正確なセグメンテーション結果が得られるようにアルゴリズムをガイドできます。

SAM一連のキュー (ポイント、境界ボックス、マスク、テキストなど) を使用してモデルを事前トレーニングし、モデルの出力を実際の出力と比較する必要があります。インタラクティブなセグメンテーションとは異なり、このタスクはあらゆるキューに対して効果的なマスクを予測するため、モデルの特定の選択と損失関数のトレーニングが必要です。

2.1.3. ゼロショット転送

事前トレーニング タスクにより、推論時にあらゆるキューに適切に応答する機能がモデルに与えられるため、適切なキューをエンジニアリングすることで下流のタスクを解決できます。

SAMあらゆるプロンプトに応答できるため、下流のタスクをプロンプトを設計するタスクに変換できます。

2.2. Anything モデルのメソッドをセグメント化する

ここに画像の説明を挿入

2.2.1. 画像エンコーダ

この論文ではMAE、高解像度の入力を処理するために最小化された事前トレーニング済みビジュアル トランスフォーマー (ViT) を使用します。ViT の詳細な説明については、私の別のブログ「CV-Model [6]: Vision Transformer」を参照してください。

この画像エンコーダーは画像ごとに 1 回実行され、モデルにヒントを与える前に適用できます。画像エンコーダのパラメータのサイズに応じて、事前トレーニング モデルの重みは、大きいものから小さいものまで、vit-h、vit-l、vit-b に分割できます。

SAMの画像エンコーダは、画像エンコーダとして標準を採用しています。元の画像はViT、比例スケーリングと短辺パディング操作を通じて1024 × 1024 1024 \times 1024のサイズを取得します。1024×1024入力画像。次に、カーネル サイズを16 1616、歩幅は16 1616の畳み込みにより、画像は64 × 64 × 768 ( W , H , C ) 64 \times 64 \times 768 (W, H, C) に離散化されます。64×64×768 W H C )ベクトル (image embedding)。WWのベクトルWCCCは順に平坦化され、複数のレイヤーに分割されますTransformer Encoderチャネル次元を削減するために、ViT出力ベクトルは 2 つの畳み込み層を通過します (カーネルは1 113 33、各層の出力アクセス256 256Layer_norm2dのフィーチャー次元に圧縮256

実装コードは次のとおりです。

self.neck = nn.Sequential(
    nn.Conv2d(
        embed_dim,
        out_chans,
        kernel_size=1,
        bias=False,
    ),
    LayerNorm2d(out_chans),
    nn.Conv2d(
        out_chans,
        out_chans,
        kernel_size=3,
        padding=1,
        bias=False,
    ),
    LayerNorm2d(out_chans),
)

MAE はコンピュータ ビジョンのためのスケーラブルな自己教師あり学習方法です. ピクセルの 95% をカバーした後でも, オブジェクトの輪郭を復元できます. 実装方法: まず入力画像のランダムな部分をマスクし, 次に失われた部分を再構築します.ピクセル

ここに画像の説明を挿入

ではMAE、元のイメージがViT重複しないパッチに分割され、一部のパッチは、ViTパッチの表現、学習されたパッチ表現、およびマスク (グレー) の表現を学習するためにアーキテクチャのエンコーダに入力するために予約されています (すべてのマスクは統合された埋め込みですが、pos の埋め込みは異なります) ) は、元のパッチ順序に従ってアーキテクチャのデコーダに入力され、ViT復元されたイメージが取得されます。損失は​​マスクの部分修復の前後ですl2_lossトレーニング後は、エンコーダーを使用して画像の特徴を抽出するだけです。

元のモデルから、画像の表現埋め込みが変更されていないことがわかります。そのため、エンコードされた画像埋め込みに対してさまざまなプロンプト入力を複数回実行して、目的の結果を得ることができます。これは、対話型セグメンテーション シナリオに非常に役立ちます。

2.2.2. プロンプトエンコーダー

分割タスクの要件に基づいて、SAM でサポートされるプロンプトは次の 2 つのカテゴリに分類できます。

スパースクラス (スパースプロンプト)

ポイントbboxフリーテキスト含まれます

ポイントは、ポイントの位置エンコーディングと、ポイントが前景にあるか背景にあるかを示す2 つの学習された埋め込みの 1 つとの合計として表されます。

位置エンコードでは、画像内の各位置に、その位置をエンコードする一意の数値ベクトルを割り当てます。これらのベクトルは、色やテクスチャなどの画像の他の特徴と結合されて、ネットワークが予測に使用できる表現を作成します。これらは本質的に、ネットワークが入力を出力により効率的にマッピングできるようにするトレーニング中に学習された一連の重みです。この場合、著者は学習したエンベディングを使用して、さまざまなタイプのキューを表現します。

プロンプトとしてポイントを指定すると、元のイメージが複数のパーツで構成されている可能性があるため、このポイントは複数のパーツに属します。この場合、デフォルトで 3 つのマスク結果 (すべて、パーツ、サブパーツ) が返されます。プロンプトとして 1 つ以上のポイントが提供されると、モデルは指定されたポイントを順番に読み取り、最後の 3 つのマスク結果から最も高いスコアを持つ予測を次の予測のプロンプトとして選択します。

ポイントの具体的なエンコード方法は次のとおりです。

point_embedding = self.pe_layer.forward_with_coords(points, self.input_image_size)
point_embedding[labels == -1] = 0.0
# self.not_a_point_embed为待学习的embedding
point_embedding[labels == -1] += self.not_a_point_embed.weight
# self.point_embeddings为待学习的embedding
point_embedding[labels == 0] += self.point_embeddings[0].weight
point_embedding[labels == 1] += self.point_embeddings[1].weight

ボックスは、一対の埋め込み (ボックスの左上隅のドットと右下隅のドット) で表されます

  • 左上隅の位置エンコーディングは、左上隅を表す学習された埋め込みと合計されます。
  • 右下隅の位置エンコーディングは、右下隅を表す学習された埋め込みと合計されます。

boxの具体的なコーディング方法は以下の通りです。

def _embed_boxes(self, boxes: torch.Tensor) -> torch.Tensor:
    """Embeds box prompts."""
    boxes = boxes + 0.5  # Shift to center of pixel
    coords = boxes.reshape(-1, 2, 2)
    corner_embedding = self.pe_layer.forward_with_coords(coords, self.input_image_size)
    corner_embedding[:, 0, :] += self.point_embeddings[2].weight
    corner_embedding[:, 1, :] += self.point_embeddings[3].weight
    return corner_embedding

CLIPフリー テキストを表すには、次のテキスト エンコーダを使用します(一般に、任意のテキスト エンコーダが可能です)。

テキスト プロンプトのオープン ソース コードのこの部分は関与していませんが、論文で言及されている実践は次のとおりです。

ここに画像の説明を挿入

  1. CLIP (ViT-L/14@336px)事前トレーニングされたテキスト エンコーダーはテキスト エンコーダーとして使用され、画像エンコーダーは SAM 画像エンコーダーを置き換えるための画像エンコーダーとして使用されます (ViT-L/14@336px出力特徴の次元は 768 ですが、ポイントと bbox の特徴の次元は 256 であるため、はまだ特徴の次元調整のための完全な接続です)、テキスト特徴ベクトルと画像特徴ベクトルはl2 norm次のステップのために準備されています
  2. 前のステップで生成されたテキストの埋め込み画像の埋め込みがマスク デコーダモジュールで位置合わせされる ようにトレーニング データを構築します。
    1. データ エンジンの第 2 段階で生成された画像を取り出します (この段階でのラベル付けの精度は高く、後述します)。これらの画像には対応するテキストの説明があり、説明テキストはテキスト埋め込みを取得するために渡されCLIPます
    2. マスク本体の最小外接長方形をランダム化1 − 2 1-212回の外部拡張とトリミングの後、画像入力に合わせてスケーリングされます (より小さい最小の外接長方形で画像を336pxフィルタリングCLIP100px
    3. 画像の主な特徴を抽出する能力を高めるために、ステップ2 22画像拡大部分50 50%0 050 の確率代わりに0を使用します。この戦略を採用すると、0 0ViTで埋められた部分をマスクします。0の位置の特徴
    4. ステップ2 2を実行します。23 33画像を取得し、埋め込まれた画像CLIPを取得します
  3. 推論段階では、テキストは最初のステップのCLIP元のテキスト エンコーダーCLIPを直接使用します (テキストでは、画像エンコーダーが画像エンコーダーを使用するか、以前のMAE事前トレーニングを使用するかを指定していないことに注意してくださいViT)

密度の高いクラス (密度の高いプロンプト)

マスクが含まれています

密集したヒント (マスクなど) は画像と空間的に対応しています。

入力画像 4 より4低い解像度入力マスクの4倍、 2 × 2 2 \times 22×2.スパンは2 22の畳み込みとダウン4 44倍、出力チャンネルは4 4416 1616最後に1 × 1 1 \times 11×1 つの畳み込みはチャネル次元を256 256256 . 各層は、GELU活性化関数と層正規化によって分離されます。次に、マスクと画像の埋め込みが要素ごとに追加されます。

粗いセグメンテーション入力が提供されない場合、空のセグメンテーション プロンプトの機能を表すためにデフォルトの学習可能な埋め込みが使用されます。

マスクの具体的なエンコード方法は次のとおりです。

self.mask_downscaling = nn.Sequential(
    nn.Conv2d(1, mask_in_chans // 4, kernel_size=2, stride=2),
    LayerNorm2d(mask_in_chans // 4),
    activation(),
    nn.Conv2d(mask_in_chans // 4, mask_in_chans, kernel_size=2, stride=2),
    LayerNorm2d(mask_in_chans),
    activation(),
    nn.Conv2d(mask_in_chans, embed_dim, kernel_size=1),
)

2.2.3. マスクデコーダ

マスク デコーダの核心は、トランスフォーマを使用して位置合わせされた画像の埋め込みと追加の画像の埋め込みを学習し、プロンプトを表示することです4 つのトークンの埋め込み。これら 4 つのトークン埋め込みは、iou トークン埋め込みと 3 つのセグメンテーション結果トークンの埋め込みです。トランスフォーマーによって学習されたトークン埋め込みは、ターゲット結果を取得するために最終タスク ヘッダーで使用されます。

トランスには 3 つの入力があります。

  • トークンの埋め込み
    • 埋め込まれているプロンプト トークンと埋め込まれている出力トークンの合計
# iou_token 1个;mask_tokens为4个,分别是3个输出结果对应的token,和一个分割sparse embedding的token
output_tokens = torch.cat([self.iou_token.weight, self.mask_tokens.weight], dim=0)
output_tokens = output_tokens.unsqueeze(0).expand(sparse_prompt_embeddings.size(0), -1, -1)
# BX(num_point+2*bbox+5) X256
tokens = torch.cat((output_tokens, sparse_prompt_embeddings), dim=1)
  • 送信元
    • 画像埋め込みと高密度プロンプト埋め込みの合計
# Expand per-image data in batch direction to be per-mask
# 对每一个token 都需要一个一样的image embedding
src = torch.repeat_interleave(image_embeddings, tokens.shape[0], dim=0)
src = src + dense_prompt_embeddings
pos_src = torch.repeat_interleave(image_pe, tokens.shape[0], dim=0)
  • pos_src
    • 画像の位置コード。ここでの位置コードも同様であることに注意してくださいDETR。2 次元コードです。xxxyyy方向は個別にコード化されてから結合されます。
    • ViTパッチの従来の一次元コーディングの代わりに、 yyが失われます。y軸方向

ここに画像の説明を挿入

具体的な実装プロセスは次のとおりです。

  • デコーダー出力のプロンプト埋め込みに学習可能なトークンを挿入します。
    1. プロンプトトークン + 自己 attn の出力トークン
    2. 取得したトークンと画像埋め込みを使用してクロスアテンドを実行します(トークンはQ)
    3. point-wise MLP 更新 token
    4. ステップ 3 の画像埋め込みとトークンを相互攻撃に使用します (Q として画像埋め込み)
  • 上記の手順2 2を繰り返します2回実行し、attn残差を介して接続し、最後にマスクと IOU スコアを出力します。
def forward(
        self, queries: Tensor, keys: Tensor, query_pe: Tensor, key_pe: Tensor
) -> Tuple[Tensor, Tensor]:
    # Self attention block
    if self.skip_first_layer_pe:
        queries = self.self_attn(q=queries, k=queries, v=queries)
    else:
        q = queries + query_pe
        attn_out = self.self_attn(q=q, k=q, v=queries)
        queries = queries + attn_out
    queries = self.norm1(queries)
    # Cross attention block, tokens attending to image embedding
    # query 为token embedding,会随着前向发生变化,query pe为最原始的token embedding
    q = queries + query_pe
    # keys 为src,key pe 为image pos embedding
    k = keys + key_pe
    attn_out = self.cross_attn_token_to_image(q=q, k=k, v=keys)
    queries = queries + attn_out
    queries = self.norm2(queries)
    # MLP block
    mlp_out = self.mlp(queries)
    queries = queries + mlp_out
    queries = self.norm3(queries)
    # Cross attention block, image embedding attending to tokens
    q = queries + query_pe
    k = keys + key_pe
    attn_out = self.cross_attn_image_to_token(q=k, k=q, v=queries)
    keys = keys + attn_out
    keys = self.norm4(keys)
    return queries, keys

3 はトランス 3 によって返されます3 つのマスク トークンの3 3mlp を3層重ねた後、位置合わせされた画像埋め込みを重ねて3 33 つの最終セグメンテーション結果。mlp を通じて iou トークンが3 33セグメンテーション結果の信頼スコア

upscaled_embedding = self.output_upscaling(src)
hyper_in_list: List[torch.Tensor] = []
for i in range(self.num_mask_tokens):
    hyper_in_list.append(self.output_hypernetworks_mlps[i](mask_tokens_out[:, i, :]))
hyper_in = torch.stack(hyper_in_list, dim=1)
b, c, h, w = upscaled_embedding.shape
masks = (hyper_in @ upscaled_embedding.view(b, c, h * w)).view(b, -1, h, w)
# Generate mask quality predictions
iou_pred = self.iou_prediction_head(iou_token_out)

2.2.4. 損失とトレーニング

モデルの損失関数は、クラスの不均衡やデータ ノイズの影響を回避するために、focal lossの線形結合です。dice loss

2.3. 何でもセグメント化データ エンジン

この記事の設計思想は の設計思想LLMと似ており、主にモデルの能力を向上させることを目的としており、この前提の下では、モデルの効果には大量のトレーニング データが不可欠です。ただし、自然言語や他の画像タスクとは異なり、セグメンテーション タスクは元の画像から自己監視することができず、セグメンテーションとラベル付けは非常にコストのかかるタスクです。したがって、この論文ではトレーニングデータを生成するための 3 つの段階を設計します。

2.3.1. 補助手動ステージ

注釈を付けるときにマスクにラベル情報を割り当てることなく、に基づくSAM対話型注釈ツールを使用して注釈を付け、最適化します。

この段階では、SAM はまず共通のパブリック セグメンテーション データ セットを通じてトレーニングされ、不正確なマスク情報を提供し、マスクを最適化します。その後、最適化後に新しく生成されたラベル付きデータのみを再トレーニングに使用します。ラベルを付けるときは、プロンプト入力として前景点と背景点を手動でクリックして、SAMラベルを付け、セグメンテーション結果を修正します。ラベル付きデータの増加に伴い、新しくラベル付けされたデータはモデルの再トレーニングに使用されます。この段階では、モデルは次のようになりますSAM。 6秒間繰り返し再訓練を受けました。

2.3.2. 半自動ステージ

まず、重要なターゲットが自動的に検出され、次にマークされていないターゲットが手動で修正されてサンプルの多様性が高まります。

検出フレームをSAMプロンプト入力として使用し (ターゲット検出はセグメンテーションよりもはるかに難易度が低い)、出力されるセグメンテーション結果において、人間は補正の信頼度スコアが低いセグメンテーション画像に注意を払うだけで済み、欠落した結果を補うことができますSAMまた、この段階では、ラベル付きデータの増加に伴い、SAMモデルの再トレーニングが継続され、合計 5 回のトレーニングが実行されます。

2.3.3. 全自動ステージ

3 番目の段階は、擬似ラベル トレーニングを生成するプロセスに似ており、大規模なデータのセグメンテーション結果を生成するためにトレーニングされた以前のデータを使用しSAM、ルールによって誤った結果の可能性を除外します。

  • 画像( 32 , 32 ) に対して ( 32 , 32 )を生成する( 32 32 )グリッド点、および各点について、有効なオブジェクトに対応すると思われるマスクのセットを予測します。
    • 点がサブパーツ、パーツ上にある場合、モデルはそのサブパーツ、パーツ、および全体のオブジェクトを返します。
  • スクリーニングマスクの信頼度を予測し、信頼度iouの高いマスクを取得
    • 安定したマスクを選択します (安定したマスク、同様のマスクでは、確率のしきい値は0.5 − δ 0.5 - \deltaです)0.5δ0.5 + δ 0.5 + \delta0.5+δ間)
  • NMS重複したマスクを確実かつ安定してフィルタリングします
    • 画像内で物体が検出される場合、検出アルゴリズムや物体の見え方の違いにより、複数回検出される場合があります。NMS は、これらの冗長な検出を削除し、最も正確な検出のみを維持するために使用されます。
    • NMS の主なアイデアは、信頼性と重複に基づいて最適なオブジェクト ボックスを選択することです。具体的には、NMS アルゴリズムの実装プロセスは次のとおりです。
      1. カテゴリごとに、すべてのオブジェクト ボックスを信頼度に従って最大から最小の順に並べ替えます。
      2. 最も信頼性の高いターゲット ボックスを選択し、それを最終結果セットに追加します。
      3. 残りのターゲット フレームと選択したターゲット フレームの間のオーバーラップを計算し (通常は IoU アルゴリズムを使用)、オーバーラップが特定のしきい値を超えるターゲット フレームを削除します。
      4. すべてのターゲット ボックスが処理されるまで、上記のプロセスを繰り返します。

3. デモ

  • プラスの点
    ここに画像の説明を挿入
    ここに画像の説明を挿入
  • マイナスポイント
    ここに画像の説明を挿入

  • ここに画像の説明を挿入
  • すべて
    ここに画像の説明を挿入

要約する

このペーパーの主な貢献は、非常に大規模で高品質のセグメンテーション データセットと、プロンプト タスクをサポートする強力な一般化を備えたモデルを構築することであり、次の特徴があります。

  • このモデルは、コンピュータ ビジョンおよび下流タスクの基礎モデルとして使用できます。
  • SAM他のコンポーネントとのインターフェースを作成することでSAM強力な統合性を実現
  • SAM汎用性と汎用性があり、リアルタイムで迅速な情報を処理できます。

参考ブログ
参考ブログ

おすすめ

転載: blog.csdn.net/HoraceYan/article/details/130420571