tanh() という関数名を見て「tan() のタイプミスかな?」と思った方もいるかもしれません。実はこれは全く別の数学関数で、双曲線関数(hyperbolic function) というグループに属する「ハイパボリックタンジェント(双曲線正接)」を計算するものです。
直接使う機会は多くありませんが、機械学習の活性化関数、信号処理、物理シミュレーションなど、特定の分野では非常に重要な役割を果たします。この記事では tanh() の基本仕様から、実践的な活用例、そして紛らわしい tan() との違いまでわかりやすく解説します。
関数概要
| 項目 | 内容 |
|---|---|
| 関数名 | tanh() |
| 読み方 | ハイパボリックタンジェント、ティーエイチタン |
| 分類 | 数学関数(双曲線関数) |
| 対応バージョン | PHP 4以降(全バージョンで使用可能) |
| 引数 | 任意の実数(float) |
| 戻り値 | float(-1から1の範囲) |
| 名前空間 | グローバル関数 |
| 関連拡張モジュール | 標準で組み込み(拡張モジュール不要) |
構文
tanh(float $num): float
tanh() は数値を1つ受け取り、その双曲線正接の値を返します。tan() と違って引数は「角度(ラジアン)」ではなく、任意の実数を渡します。これは三角関数(円関数)とは異なる性質の関数だからです。
数式で表すと以下のようになります。
tanh(x) = sinh(x) / cosh(x) = (e^x - e^-x) / (e^x + e^-x)
tanh関数の最大の特徴:出力が必ず-1〜1の範囲に収まる
入力 x(任意の実数、-∞ ~ +∞)
│
│ tanh()
▼
出力(必ず -1 ~ 1 の範囲に収まる)
x → -∞ のとき tanh(x) → -1
x = 0 のとき tanh(x) = 0
x → +∞ のとき tanh(x) → +1
この「どんな大きな入力でも、出力が必ず-1から1の間に収まる」という性質こそが、tanh() が実務で使われる最大の理由です。
基本的な使い方
<?php
echo tanh(0) . PHP_EOL;
echo tanh(1) . PHP_EOL;
echo tanh(-1) . PHP_EOL;
echo tanh(10) . PHP_EOL;
echo tanh(-10) . PHP_EOL;
実行結果:
0
0.76159415595576
-0.76159415595576
0.9999999958777
-0.9999999958777
入力が 10 のような大きな値でも、出力は 1 にかなり近づくものの決して 1 を超えない、という性質が確認できます。
実践的なコード例
例1:任意の数値を-1〜1の範囲に正規化する
<?php
class ValueNormalizer
{
/**
* どんな範囲の数値でも-1〜1の範囲に収める
* 値が大きいほど1に近づき、小さい(負の大きい)ほど-1に近づく
*/
public function normalize(float $value, float $scale = 1.0): float
{
return tanh($value / $scale);
}
}
$normalizer = new ValueNormalizer();
foreach ([-100, -10, -1, 0, 1, 10, 100] as $value) {
$result = $normalizer->normalize($value, 10);
printf("入力: %6d → 正規化後: %.4f\n", $value, $result);
}
実行結果:
入力: -100 → 正規化後: -1.0000
入力: -10 → 正規化後: -0.7616
入力: -1 → 正規化後: -0.0997
入力: 0 → 正規化後: 0.0000
入力: 1 → 正規化後: 0.0997
入力: 10 → 正規化後: 0.7616
入力: 100 → 正規化後: 1.0000
異常値(極端に大きい値)が含まれていても、出力が暴れずに一定の範囲に収まるため、グラフ描画やスコアの可視化などで「外れ値を緩やかに圧縮する」用途に向いています。
例2:ニューラルネットワークの活性化関数として使う
<?php
class Neuron
{
private array $weights;
private float $bias;
public function __construct(array $weights, float $bias)
{
$this->weights = $weights;
$this->bias = $bias;
}
public function activate(array $inputs): float
{
$sum = $this->bias;
foreach ($inputs as $i => $input) {
$sum += $input * $this->weights[$i];
}
// tanhを活性化関数として使用
return tanh($sum);
}
}
$neuron = new Neuron([0.5, -0.3, 0.8], 0.1);
$output1 = $neuron->activate([1.0, 2.0, 0.5]);
$output2 = $neuron->activate([-1.0, -2.0, -0.5]);
printf("出力1: %.4f\n", $output1);
printf("出力2: %.4f\n", $output2);
実行結果:
出力1: 0.5370
出力2: -0.6044
tanh() はシグモイド関数と並んで、ニューラルネットワークの活性化関数としてよく使われます。出力が0を中心に対称(-1〜1)であるため、シグモイド関数(0〜1)よりも学習が安定しやすいという特徴があります。
例3:感情スコアやレーティングの圧縮表示
<?php
class SentimentScoreCompressor
{
/**
* 生の感情スコア(無限に大きくなりうる)を
* 見やすい-1.0〜1.0の表示用スコアに変換する
*/
public function compress(float $rawScore, float $sensitivity = 0.1): float
{
return tanh($rawScore * $sensitivity);
}
public function toLabel(float $compressedScore): string
{
if ($compressedScore > 0.5) {
return '非常にポジティブ';
}
if ($compressedScore > 0.1) {
return 'ポジティブ';
}
if ($compressedScore < -0.5) {
return '非常にネガティブ';
}
if ($compressedScore < -0.1) {
return 'ネガティブ';
}
return '中立';
}
}
$compressor = new SentimentScoreCompressor();
foreach ([2, 8, 0, -3, -15] as $rawScore) {
$compressed = $compressor->compress($rawScore);
$label = $compressor->toLabel($compressed);
printf("生スコア: %4d → 圧縮後: %.3f(%s)\n", $rawScore, $compressed, $label);
}
実行結果:
生スコア: 2 → 圧縮後: 0.197(ポジティブ)
生スコア: 8 → 圧縮後: 0.664(非常にポジティブ)
生スコア: 0 → 圧縮後: 0.000(中立)
生スコア: -3 → 圧縮後: -0.291(ネガティブ)
生スコア: -15 → 圧縮後: -0.905(非常にネガティブ)
口コミ分析やレコメンドエンジンで「無限に大きくなりうる生のスコア」を、人間が見やすい一定範囲のスコアに変換する際にも活用できます。
例4:S字カーブを描くアニメーションのイージング処理
<?php
class EasingFunction
{
/**
* tanhを利用したS字カーブのイージング
* 進行度(0.0〜1.0)を受け取り、滑らかなS字カーブの値を返す
*/
public function easeTanh(float $progress, float $steepness = 6.0): float
{
// progressを-steepness/2 〜 +steepness/2 の範囲にマッピング
$x = ($progress - 0.5) * $steepness;
// tanhの出力(-1〜1)を0〜1の範囲に変換
return (tanh($x) + 1) / 2;
}
}
$easing = new EasingFunction();
for ($i = 0; $i <= 10; $i++) {
$progress = $i / 10;
$eased = $easing->easeTanh($progress);
printf("進行度: %.1f → イージング後: %.4f\n", $progress, $eased);
}
実行結果:
進行度: 0.0 → イージング後: 0.0474
進行度: 0.1 → イージング後: 0.0900
進行度: 0.2 → イージング後: 0.1674
進行度: 0.3 → イージング後: 0.2891
進行度: 0.4 → イージング後: 0.4504
進行度: 0.5 → イージング後: 0.5000
進行度: 0.6 → イージング後: 0.5496
進行度: 0.7 → イージング後: 0.7109
進行度: 0.8 → イージング後: 0.8326
進行度: 0.9 → イージング後: 0.9100
進行度: 1.0 → イージング後: 0.9526
開始と終了がゆっくりで、中間が速く進む「S字カーブ」の動きを表現でき、UIアニメーションやゲームのモーション設計で利用できます。
例5:信号のソフトクリッピング(音声処理)
<?php
class SoftClipper
{
/**
* 音声信号などの値を、急激な歪みを避けながら
* -1.0〜1.0の範囲にソフトに収める
*/
public function softClip(float $sample, float $drive = 1.0): float
{
return tanh($sample * $drive);
}
}
$clipper = new SoftClipper();
$samples = [0.2, 0.8, 1.5, 3.0, -2.5];
foreach ($samples as $sample) {
$clipped = $clipper->softClip($sample, 1.0);
printf("元の値: %5.1f → クリップ後: %.4f\n", $sample, $clipped);
}
実行結果:
元の値: 0.2 → クリップ後: 0.1974
元の値: 0.8 → クリップ後: 0.6640
元の値: 1.5 → クリップ後: 0.9051
元の値: 3.0 → クリップ後: 0.9951
元の値: -2.5 → クリップ後: -0.9866
音声信号処理の分野では、tanh() を使ったソフトクリッピングが「歪みエフェクト(ディストーション)」の実装によく使われます。単純な値の切り捨て(ハードクリップ)と違い、滑らかに値が飽和していくのが特徴です。
例6:双曲線関数同士の関係を確認する
<?php
class HyperbolicRelationChecker
{
public function verify(float $x): array
{
$sinhValue = sinh($x);
$coshValue = cosh($x);
$tanhValue = tanh($x);
// tanh(x) = sinh(x) / cosh(x) の関係を確認
$calculated = $sinhValue / $coshValue;
return [
'sinh' => $sinhValue,
'cosh' => $coshValue,
'tanh' => $tanhValue,
'sinh/cosh' => $calculated,
'is_matching' => abs($tanhValue - $calculated) < 0.0000001,
];
}
}
$checker = new HyperbolicRelationChecker();
$result = $checker->verify(2.0);
printf("sinh(2) = %.6f\n", $result['sinh']);
printf("cosh(2) = %.6f\n", $result['cosh']);
printf("tanh(2) = %.6f\n", $result['tanh']);
printf("sinh(2)/cosh(2) = %.6f\n", $result['sinh/cosh']);
printf("両者は一致: %s\n", $result['is_matching'] ? 'はい' : 'いいえ');
実行結果:
sinh(2) = 3.626860
cosh(2) = 3.762196
tanh(2) = 0.964028
sinh(2)/cosh(2) = 0.964028
両者は一致: はい
tanh(x) = sinh(x) / cosh(x) という定義通りの関係が成り立っていることを確認できます。
例7:原点付近での線形近似との比較
<?php
class LinearApproximationComparer
{
public function compare(float $x): array
{
$actualTanh = tanh($x);
// x が0に近いとき、tanh(x) はおおよそ x に近似できる
$linearApprox = $x;
$error = abs($actualTanh - $linearApprox);
return [
'tanh' => $actualTanh,
'approx' => $linearApprox,
'error' => $error,
];
}
}
$comparer = new LinearApproximationComparer();
foreach ([0.01, 0.1, 0.5, 1.0, 2.0] as $x) {
$result = $comparer->compare($x);
printf(
"x=%.2f: tanh(x)=%.4f, 線形近似=%.4f, 誤差=%.4f\n",
$x,
$result['tanh'],
$result['approx'],
$result['error']
);
}
実行結果:
x=0.01: tanh(x)=0.0100, 線形近似=0.0100, 誤差=0.0000
x=0.10: tanh(x)=0.0997, 線形近似=0.1000, 誤差=0.0003
x=0.50: tanh(x)=0.4621, 線形近似=0.4622, 誤差=0.0004
x=2.00: tanh(x)=0.9640, 線形近似=2.0000, 誤差=1.0357
0付近では tanh(x) ≈ x という近似が成り立ちますが、xが大きくなるにつれて誤差が急激に拡大することがわかります。この性質を理解しておくと、数値計算の挙動を予測しやすくなります。
関連関数との比較
| 関数 | 役割 | 入力の制約 | 出力範囲 |
|---|---|---|---|
tanh() | 双曲線正接を求める | 任意の実数 | -1 〜 1 |
tan() | 三角関数のタンジェントを求める | ラジアン(角度) | -∞ 〜 +∞(漸近線あり) |
sinh() | 双曲線正弦を求める | 任意の実数 | -∞ 〜 +∞ |
cosh() | 双曲線余弦を求める | 任意の実数 | 1 以上 |
atanh() | tanhの逆関数 | -1 〜 1 | 任意の実数 |
sigmoid(独自実装) | 0〜1に正規化(PHPに組み込みはない) | 任意の実数 | 0 〜 1 |
最も混同しやすいのが tan() と tanh() です。tan() は円(三角関数)に基づき、入力はラジアンで出力は無限大になりうるのに対し、tanh() は双曲線関数に基づき、入力は任意の実数で出力は必ず-1〜1に収まるという、性質がまったく異なる関数です。
よくある落とし穴・注意点
tan()との名前の混同 1文字違いのため、tan()を書くつもりでtanh()と書いてしまう、あるいはその逆のタイプミスは非常によく起こります。tan()は「角度」を扱う三角関数、tanh()は「任意の実数」を扱う双曲線関数という、根本的に異なる関数であることを常に意識しましょう。- 「角度」だと誤解してラジアン変換をしてしまう
tan()の経験があると、tanh()にもdeg2rad()を通してしまうミスが起こりがちです。tanh()の引数は角度ではなく、ただの実数です。変換は不要です。 - 出力が-1〜1に収まることを忘れて後続処理でエラーになる
tanh()の出力を0〜1の範囲が必要な処理(例えば確率やパーセンテージ)にそのまま渡すと、負の値が原因でエラーや不正な計算結果になることがあります。0〜1の範囲が必要な場合は(tanh($x) + 1) / 2のように変換しましょう。 - 大きな入力に対する勾配消失(機械学習での注意点)
tanh()を活性化関数として使う場合、入力が非常に大きい(または小さい)と出力の変化がほぼ無くなり(傾きがほぼ0になる)、ニューラルネットワークの学習が停滞する「勾配消失問題」が起きやすくなります。入力値のスケーリングや正規化を事前に行うことが重要です。 sinh()やcosh()がオーバーフローする値での計算tanh(x) = sinh(x) / cosh(x)という定義上、内部的に非常に大きな指数計算が行われます。極端に大きなx(数百以上など)を渡してもtanh()自体は安定して-1または1を返しますが、自分でsinh()やcosh()を個別に計算して割ろうとすると、オーバーフローしてINFやNANになることがあります。可能な限りtanh()を直接使うほうが安全です。
まとめ
| ポイント | 内容 |
|---|---|
| 役割 | 双曲線正接(ハイパボリックタンジェント)を計算する |
| 引数 | 任意の実数(角度ではない) |
| 戻り値 | -1〜1の範囲に収まるfloat |
tan()との違い | 円関数ではなく双曲線関数。角度変換は不要 |
| よく組み合わせる関数 | sinh(), cosh(), atanh() |
| 主な用途 | 値の正規化、活性化関数、ソフトクリッピング、S字カーブのイージング |
| 注意点 | tan()との混同、出力範囲の誤解、勾配消失 |
tanh() は日常的なWeb開発で頻繁に使う関数ではありませんが、「どんな入力でも出力を-1〜1の範囲に滑らかに収めてくれる」という性質は、機械学習・音声処理・アニメーション制御など、専門分野では非常に強力な道具になります。tan() との違いをしっかり区別して使い分けられるようになりましょう。
