【極めればこのテンソル操作 】tensor.unsqueeze(0)と array[None] の違い

【極めればこのテンソル操作 】tensor.unsqueeze(0)と array[None] の違い
Photo by Merve Sehirli Nasir / Unsplash

今日は、 unsqueeze(0) の解説しつつ、私たちがよく直面する「あるある」な問題についてもちょこっと話してみたいと思います。

「value.unsqueeze(0)」と「value[None]」 の見分けついていますか?


はい、前者は主に PyTorch、後者は NumPyでの操作の違いです。

でもどちらも、ぱっとみは、先頭に新しく次元を追加する操作なので、コードをちらっとみただけではわからないことがありますよね。

なぜかというと、ディープラーニング系のプログラミングでは PyTorchのテンソルと、NumPyの配列操作がかなり入り混じるからです。

そう、今日の話題はPyTorchとNumPyのコードが入り乱れて、どっちの配列(テンソル)を扱っているのわけワカメになる問題です。

ちなみに、話題のテーマをブラさないように PyTorchでは 先頭に新しい次元を追加するときに unsqueeze(0) だけでなく [None] も使えてしまいますが、いったん[None]は NumPy で主に使用する操作という前提で説明させてくださいませ。^^;

これに対する当社なりの処方箋は、また別投稿をしたいとおもいますが、両者が無邪気に入り混じらないように、PyTorchとNumPyのコードをなるべく分離するようにしています。例えば「同一関数、メソッド内はPyTorchかNumPyに寄せる」、や、「GPU投入寸前までPyTorchテンソル化をガマンしてNumPyでがんばる」など、(涙ぐましい?)現場の工夫をしています^^

NumPy系の変数名には「なんちゃら_numpy」「なんちゃら_tensor」のようにするなど、あまりにも紛らわしいときには、行っていますが、型宣言のゆるいPythonコーディングの慣例上、同一変数名なのにNumPyからPyTorchにいつのまにか変わっていた、なんていう外部コードも大量にあり、なかなか難しいですね。

PyTorchとNumPyが入り乱れる世界 ~機械学習プロジェクトを進めていると、こんな経験ありませんか?

  • データの前処理はNumPyで行っていたのに、モデルに入力するときにはPyTorchのテンソルに変換しなければならない。
  • モデルから出力されたPyTorchのテンソルを、可視化のためにNumPy配列に戻す。
  • そして気づいたら、コード内でNumPyとPyTorchの関数が混在している...

これって、まるでプログラミング言語のバベルの塔ですよね。

今回は、PyTorchの.unsqueeze(0)メソッドとNumPyの[None]インデックスの違いについて詳しく見ていきましょう。一見似ているこれらの操作ですが、実は重要な違いがあります。

1. 基本的な違い

まず、最も基本的な違いは、冒頭でふれたとおり、

  • .unsqueeze(0): PyTorchのテンソルに使用されるメソッドです。
  • [None]: NumPy配列やPythonのリストに使用されるインデックス操作です。
    (コラムに書きましたが、実はPyTorchでも使えちゃいますが、頭に次元追加する操作は PyTorchでは unsqueeze(0)、おしりに次元追加する操作はunsqueeze(-1)でやるのが可読性や操作意図のわかりやすからオススメです)

2. 動作の詳細

.unsqueeze(0)

PyTorchの.unsqueeze(0)メソッドは、テンソルの0次元目(先頭)に新しい次元を追加します。これは、バッチ処理のためにデータを準備する際によく使用されます。1件だけのデータを学習モデルに突っ込みたいときも、「バッチ次元」を求められることが常なので unsqueeze(0) は頻発するコードだとおもいます。

import torch

x = torch.tensor([1, 2, 3])
print(x.shape)  # torch.Size([3])

x_unsqueezed = x.unsqueeze(0)
print(x_unsqueezed.shape)  # torch.Size([1, 3])

[None]

NumPyの[None]インデックスは、配列に新しい軸を追加します。これも実質的に次元を1つ増やすことになります。

例:

import numpy as np

y = np.array([1, 2, 3])
print(y.shape)  # (3,)

y_expanded = y[None]
print(y_expanded.shape)  # (1, 3)

3. 柔軟性の違い

.unsqueeze(n)メソッドは、引数nを変えることで任意の位置に次元を追加できる柔軟性があります。

例:

import torch

z = torch.tensor([[1, 2], [3, 4]])
print(z.shape)  # torch.Size([2, 2])

z_unsqueezed_0 = z.unsqueeze(0)
print(z_unsqueezed_0.shape)  # torch.Size([1, 2, 2])

z_unsqueezed_1 = z.unsqueeze(1)
print(z_unsqueezed_1.shape)  # torch.Size([2, 1, 2])

一方、[None]は常に新しい軸を先頭(axis 0)に追加します。ただし、NumPyにはnp.expand_dims()関数があり、これを使用すると任意の位置に次元を追加できます。

import numpy as np

w = np.array([[1, 2], [3, 4]])
print(w.shape)  # (2, 2)

w_expanded_0 = np.expand_dims(w, axis=0)
print(w_expanded_0.shape)  # (1, 2, 2)

w_expanded_1 = np.expand_dims(w, axis=1)
print(w_expanded_1.shape)  # (2, 1, 2)

4. パフォーマンスの考慮

一般的に、.unsqueeze()[None](またはnp.expand_dims())の間にパフォーマンスの大きな差はありません。しかし、大規模なデータセットや複雑なモデルを扱う場合、わずかな違いが積み重なって影響を与える可能性があります。

PyTorchを使用している場合は.unsqueeze()を、NumPyを使用している場合は[None]np.expand_dims()を使用するのが自然で効率的です。

まとめ ~.unsqueeze(0)[None]の実践的理解~

今回は、.unsqueeze(0)[None]の用法について詳しく解説しました。

問題の本質は、PyTorchとNumPyの混在にありますが、コードを書く上では、どちらの「世界」にいるのかを常に意識することが大切ですね。

コードを読む際には、.unsqueeze(0)が登場したら「ここからPyTorchでの次元追加だな」と考え、[None]を見たら「まだNumPyの領域にいるな」と理解するとよいでしょう。

使用シーンの違いも重要なポイントです。.unsqueeze(0)は多くの場合、1件データのモデル投入の直前に「緊急的な」次元追加として用いられます。そのため、モデル投入直前でよく目にすることになります。一方、[None]による次元追加は、通常モデル投入よりもずっと前の段階、つまりまだNumPy操作のフェーズで行われることが多いです。その後、モデル投入直前でPyTorchテンソルへの変換とGPUへの送り込みが行われるというパターンもよく見かけます。

これらの操作を見かけたら、まずは「バッチ次元追加かな?」と推測してみるのが良いでしょう。バッチ処理のニーズで使われることが多いためです。ただし、必ずしもバッチ次元の追加だけでなく、例えば画像処理ではチャンネル次元の追加に使われることもあるので、コンテキストをよく確認することが大切です。

結論として、.unsqueeze(0)[None]の違いを理解し、適切に使い分けることで、より明確で効率的なコードを書くことができます。また、これらの操作を見かけたときは「バッチ次元の追加かもしれない」と考えつつ、常にコンテキストを確認する習慣をつけることで、コードの意図をより深く理解できるようになるでしょう。

Read more

TensorRT 10 × Blackwell 移行ガイド【中編】ビルドが通っても正しいとは限らない — 沈黙劣化 5 連発

TensorRT 10 × Blackwell 移行ガイド【中編】ビルドが通っても正しいとは限らない — 沈黙劣化 5 連発

こんにちは! 前回の記事「TensorRT 10 × Blackwell 移行ガイド【前編】RTX 50 で推論資産が動かない — 基本と最初の壁」では、Blackwell 世代への移行で既存の推論資産が動かなくなる理由と、TensorRT 10 化を最小構成で通す手順を扱いました。前編で出てきた問題には、実はひとつ共通点があります。 すべて、エラーで止まってくれたということです。 本当に怖いのはその先です。TensorRT への移行パイプラインには、 * ビルドが通る * 実行も通る * 速度もちゃんと出る * 出力の形も、値も、一見それらしい * なのに、中身が間違っている という失敗の仕方が存在します。 本記事では、TensorRT 本体の挙動だけでなく、export 時のミス・精度設定・エンジンの配布ミスまで含めて、エラーで停止せず誤った出力へ至る事象を便宜上 「沈黙劣化」 と呼びます。 テストが通り、ベンチマークが良い数字を出し、「◯倍速くなりました」と報告した後になって発覚するので、非常に厄介です。 本記事では、実際に踏んだ

By Qualiteg プロダクト開発部
Claude Opus 5.0 完全ガイド モデル仕様とAPI・Claude Code運用ポイント

Claude Opus 5.0 完全ガイド モデル仕様とAPI・Claude Code運用ポイント

こんにちは! 2026年7月24日、AnthropicからClaude Opus 5がリリースされました。 Opus 4.8(5月28日リリース)からわずか2ヶ月での世代交代です。このあたりのスピード感、加速していますね。 さて、当ブログではClaude Opus 4.7 完全ガイド、Claude Opus 4.8 完全ガイドとOpusの世代を追いかけてきましたが、今回のOpus 5は過去2回の「4.x内のアップデート」とは立て付けが根本的に違います。 何が違うのか。まず、Opus 5は「最上位モデル」ではありません。 Anthropicのラインナップには2026年6月9日リリースのClaude Fable 5が最上位として存在し、Opus 5はその下位、Sonnet 5の上位という「中上位」ポジションで投入されました。 Opusという名前が「最上位ティア」を意味した時代は、Fable 5の登場で終わっています。 そのうえでAnthropicはOpus 5を「

By Qualiteg プロダクト開発部
TensorRT 10 × Blackwell 移行ガイド【前編】RTX 50 で推論資産が動かない — 基本と最初の壁

TensorRT 10 × Blackwell 移行ガイド【前編】RTX 50 で推論資産が動かない — 基本と最初の壁

こんにちは! 新しい GPU を手に入れてワクワクしながら既存の推論環境を載せ替えたら、 昨日まで普通に動いていたものが軒並みエラーで止まった そんな経験はないでしょうか。NVIDIA RTX 50 系、NVIDIA RTX PRO 系(Blackwell 世代)への移行では、これがかなりの高確率で起きます。 そして厄介なことに、エラーで止まってくれるのは、まだ親切なほうで、、TensorRT の世界には 「ビルドは通る、実行も通る、速度もちゃんと出る、けれど出力だけが静かに壊れている」 という、いちばん見つけにくい失敗の仕方が存在します。 本記事はその全体像を扱うシリーズの前編です。 対象環境 OS: Ubuntu 24.04 (WLS) GPU: NVIDIA RTX PRO 4000 Blackwell・GeForce RTX 5060 Ti (ともに Compute Capability 12.

By Qualiteg プロダクト開発部
Kimi K3 徹底リサーチ — 2.8兆パラメータ、「史上最大のオープンウェイト」は実現するか

Kimi K3 徹底リサーチ — 2.8兆パラメータ、「史上最大のオープンウェイト」は実現するか

こんにちは! 2026年7月16日、中国・北京の Moonshot AI が新しいフラッグシップモデル Kimi K3 を発表し、APIやWebサービスでの提供を開始しました。 総パラメータ2.8兆という規模、100万トークンのコンテキスト、そして 「史上最大のオープンウェイトモデルになる」 という宣言がAI界隈をにぎわせています。 当ブログでは今年5月の記事「Mythos(ミュトス)レベルのオープンモデルはいつ出るのか」で、オープンモデルがクローズドのフロンティアにいつ追いつくのかを予測しました。 Kimi K3 は、まさにその問いに対する現時点での最新の「回答」のひとつです。 一方で、この記事を書いている7月20日時点では、モデルのウェイトも技術レポートもまだ公開されていません。 ただし、XなどSNSかいわいでは、 「ガードレールが弱めで、Fable5では拒否されるようなプロンプトでも対応してくれる」 「すぐにOpus4.8にフォールバックする Fable5より使い勝手がいい」 といった声が散見されており、 米国産のガードレール強め方針にたいして、ガードレール

By Qualiteg プロダクト開発部