[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ アーティクル Pinterestの広告ランキングの仕組みを解き明かす

Pinterestの広告ランキングの仕組みを解き明かす

キーポイント

  • ディープラーニングに基づく機械学習アルゴリズムは、レスポンスが良くパーソナライズされた広告レコメンデーションのために活用されている。

  • 広告プラットフォームの目的は、長期的にユーザー、広告主、プラットフォームの価値を最大化することである。

  • 広告配信ファネルは、候補の取得、重み付けのランキング、オークション、割り当てで構成され、高いQPSで低遅延配信を保証する。

  • 時間をかけて、Pinterestは機械学習モデルを従来のアプローチから、パーソナライゼーションを強化するディープニューラルネットワーク(DNN)やトランスフォーマーアーキテクチャなどのより複雑なものへと進化させてきた。

  • 継続的インテグレーションとデプロイメント(CI/CD)、モデルのバージョニング、テスト、モニタリングなどの堅牢なMLOpsプラクティスは、迅速かつ効果的な反復に不可欠である。

原文リンク(2024-03-26)

PinterestのStaff Machine Learning EngineerであるAayush Mudgal氏は、QCon San Francisco 2023でUnpacking how Ads Ranking Works at Pinterestというセッションを行った。その中で彼は、Pinterestがどのようにディープラーニングとビッグデータを使って、ユーザーに関連する広告をカスタマイズしているかを説明した。

多くのオンライン・プラットフォームと同様に、パーソナライズされた体験がPinterestの中心である。このパーソナライズされた体験は、様々な機械学習(ML)アプリケーションによって実現されている。これらのアプリケーションはそれぞれ、プラットフォームが収集した大規模なデータから複雑なウェブパターンを学習しようとしている。

Mudgal氏は講演で、この体験の一部である広告配信に焦点を当てた。彼は、大規模な広告配信に機械学習がどのように使われているかを詳しく説明した。続いて、広告マーケットプレイスと広告配信ファネルについて説明し、広告配信アーキテクチャの典型的な部分について語り、広告検索とランキングという2つの主要な問題に踏み込んだ。最後に、モデルのトレーニング中にシステムの健全性を監視する方法について説明し、大規模モデル配信の課題と解決策について締めくくった。

コンテンツのレコメンド

Mudgal氏はまず、コンテンツ・レコメンデーション・システムの特徴を示した。どのソーシャルメディア・プラットフォームにも、ユーザーに見せる可能性のあるコンテンツが何百万、何十億とある。目標は、特定のユーザーに関連するアイテムを見つけることだが、コンテンツカタログとユーザーベースが非常に大きいため、Pinterestのようなプラットフォームでは、各ユーザーに対する各コンテンツアイテムの関連確率を事前に 計算できない。

その代わり、この確率を数百ミリ秒以内という速さで予測できるシステムが必要となる。また、高い1秒あたりのクエリー数(QPS)にも対応しなければならない。最後に、時間とともに変化するユーザーの興味に対応する必要がある。このようなニュアンスをすべて把握するために、プラットフォームはレコメンデーションシステムが複数目的の最適化問題を解くことを確認する必要がある。

ユーザーがプラットフォーム上の特定の要素を操作すると、多くの場合、様々な類似コンテンツが提示される。これは、ターゲット広告が登場する重要な瞬間である。これらの広告は、プラットフォーム内のユーザーと広告主のコンテンツのギャップを埋めることを目的としている。その目的は、関連性の高いコンテンツでユーザーを引き付け、プラットフォームから広告主のウェブサイトに誘導することだ。

これは双方向の市場である。Pinterest、Meta、Googleのような広告プラットフォームは、ユーザーと広告主や関連コンテンツを結びつける手助けをする。ユーザーはコンテンツに関与するためにプラットフォームを訪問する。広告主は、ユーザーがコンテンツを表示してユーザーに興味を持ってもらうために、これらの広告プラットフォーム料金を支払う。プラットフォームは、ユーザー、広告主、プラットフォームの価値を最大化したいと考えている。

広告マーケットプレイス

広告主は、自社のコンテンツをユーザーに見せたいと考えている。それは、そのブランドの認知度を高めたり、プラットフォーム上でより多くのクリックを促したりするような単純なことかもしれない。その際、広告主はプラットフォームに表示される特定の広告にどれだけの価値を見出すかを選択もできる。

広告主は、主に2つの入札戦略から選択できる。1つは、広告主がプラットフォームを通じて生成された各インプレッションやインタラクションに対して、あらかじめ決められた金額を支払う方法だ。あるいは、広告主は決められた予算を設定し自動入札プロセスを通じ、最適な予算配分をプラットフォームのアルゴリズムに任せることもできる。

次に、広告主はクリエイティブや画像コンテンツを選択する。クリエイティブを配信する前に、広告プラットフォームは、この特定のコンテンツをユーザーに配信することを決定するのに適した確率スコアを定義する必要がある。これは、クリック予測として定義できる。あるユーザーと、彼らのプラットフォーム上での行動を考慮した場合、このユーザーがコンテンツをクリックする確率はどれくらいだろうか?

しかし、クリック数を最大化することは、プラットフォーム上で関連性が最適化されるとは限らない。スパムコンテンツを促進する可能性もある。プラットフォームは、「良い」クリック、非表示、保存、リポストなどのシャドウ予測を行うこともあり、総合的な方法でユーザージャーニーを捉えようとしている。プラットフォームによっては、コンバージョンの最適化のような、広告主のウェブサイト上でより多くの販売を促進しようとする広告目標があるかもしれない。

また、プラットフォームが、動画やコレクションなど、より多くのコンテンツタイプにシステムを拡張したいと考えているとする。ここで示したような予測を立てるだけでなく、プラットフォーム上での優れた動画視聴とは何かを理解する必要もある。

最後に、異なるプラットフォームの表面には異なるコンテキストもある。ユーザーのホームフィードのように、プラットフォームがその時点でコンテキストや関連性の情報を持っていない場合もあれば、検索クエリのように、ユーザーが意図を持っている場合もある。

このような複雑さを考えると、プラットフォームがスケールするにつれて、パフォーマンスよくこれらすべての予測を行えるようにする必要がある。ここで決定された設計のいくつかは、スケーリングと製品の成長をサポートするためのものでもある。

広告配信インフラ

Mudgal氏は次に、Pinterestにおける広告配信インフラのハイレベルな概要を紹介した。ユーザーがプラットフォームにアクセスすると、プラットフォームはユーザーに見せたいコンテンツをフェッチする必要がある。ユーザーのリクエストはロードバランサーを経由してアプリサーバーに渡される。そして、ユーザーのフィードに挿入される広告を返す広告サーバーに渡される。

図1:広告配信インフラのハイレベルな概要

広告サーバーは、エンド・ツー・エンドで数百ミリ秒程度の非常に低遅延な方法でこれを行う必要がある。広告サーバーへの入力は、通常、ユーザーID、ユーザーのIPアドレス、時刻など、かなりまばらである。

最初のタスクは、このユーザーの特徴を取得することである。これは、IPアドレスからユーザーの位置情報を取得したり、過去にこのユーザーがプラットフォーム上でどのようなやり取りをしたかを取得したりする。これらは通常、キーが「ユーザーID」でバリューが「特徴」である、キーバリューストアから取得される。

このシステムが特徴空間をエンリッチした後、これらは候補検索フェーズに渡される。候補検索フェーズでは、何十億ものコンテンツ・アイテムの中から最適な候補セットを見つけようとふるいにかけ、ユーザーに表示できる何百、何千もの候補を見つけ出そうとする。次に、これらはランキングサービスに渡され、重みづけモデルを使用して、複数の目的 (クリック、グッドクリック、保存、再投稿、非表示) にわたって、ユーザーがコンテンツとインタラクションする確率が決定される。

このランキングサービスは、通常、特徴抽出にもアクセスできる。なぜなら、システムは、ランキングリクエストの候補に含まれるすべてのコンテンツの特徴をパフォーマンスよく送信できないからだ。通常、数百から数千の候補がランキングサービスに送信され、それらのすべての特徴を一緒に送信すると、リクエストが肥大化する。

その代わりに、これらの特徴はローカルのインメモリキャッシュ(leveldbのようなもの)を通してフェッチされ、キャッシュヒットを最大化するために、外部ルーティングレイヤーを利用できる。最後に、ランキング・サービスは広告を広告サーバーに送り返す。

ほとんどの従来の機械学習システムでは、特定の時間を通してその広告を表示するために使用される特徴の値は、機械学習モデルを訓練するために非常に重要である。これらの特徴をフェッチする同期リクエストに加えて、それらをログに記録する特徴ロギングサービスに送られる非同期リクエストもある。また、システムのパフォーマンスを向上させるために、フォールバック候補が用意されている。システムのどこかが故障したり、候補を取得できなかったりした場合、フォールバック候補をユーザーに表示することで、ユーザーは常にプラットフォーム上のコンテンツを見ることができる。

広告サーバーから広告コンテンツが返され、ユーザーのフィードに挿入される。ユーザーがフィードとインタラクトする際に、イベント・ロギング・サービスが存在し、Apache Kafkaを使用することで、これらのイベントすべてをリアルタイムでログに記録できる。このイベント・ロギング・サービスは非常に重要である。なぜなら、広告主は、ユーザーがインタラクションしたり、広告をクリックしたりした場合に課金されるからだ。

さらに、広告主は1日に使える最大予算を定義しているため、リアルタイムで課金されなければならない。ロギングパイプラインがリアルタイムのパフォーマンスを持っていない場合、プラットフォームは広告主の予算を超過したり、広告主に無料の インプレッションを返したりするかもしれない。

イベント・ロギングのパイプラインは、1時間ごとや1日ごとのモニタリングシステムを含むレポーティングシステムにもフィードされる。このレポーティングシステムは、ロギングされる機能とも連動している。なぜなら、プラットフォームは、国、年齢、またはプラットフォーム上にあるかもしれない他の機能など、さまざまな機能に関する広告パフォーマンスに関するデータを広告主に見せたいからだ。最後に、イベント・ロギング・サービスとフィーチャーロガーは、Pinterestのすべての機械学習モデルの学習データを結合する。

広告配信ファネル

Mudgal氏は次に、広告配信ファネルをより詳細に示した。これは、検索、ランキング、オークションの3つのステップに分かれている。検索ステップでは、何百万もの候補ジェネレーターが並行して動作している。リクエストが与えられると、彼らのモチベーションは広告候補のベストセットを得ることだ。これは、新鮮なコンテンツ、ユーザーの最近のインタラクション、埋め込みベースのジェネレーターなど、いくつかの基準に基づいている。そして、候補はランキングモデルに渡され、ランキングモデルは先に説明した様々なエンゲージメントを予測しようとする。

図2:広告配信ファネル

これらの予測が与えられると、オークションのステップで、全体のコンテキストの中で特定の広告をユーザーに提供する価値が決定される。その広告の価値に応じて、プラットフォームはユーザーにその広告を見せるかどうかを決めることができる。また、異なるビジネスロジックや割り当てに関する制約もこの時点で扱うことができる。例えば、プラットフォームはフィード内で2つの広告を一緒にしておくべきか、それとも分けておくべきか?

広告の検索

広告検索の主な動機は、最良の広告候補を最良の効率で選択することである。このプロセスでは、非常に軽量なMLモデルを使用し、非常に低い計算コストで実行できる。モデルの品質指標は想起である。

このシステムへの入力は、ユーザーのIDとコンテンツIDとリクエストレベルの特徴であることを忘れてはならない。検索にはシグナルエンリッチメントが必要であり、これはいくつかのグラフベースのエキスパンダを使用して、キーバリュー特徴ストアから余分な特徴をフェッチする。例えば、ユーザーIDは、年齢、場所、性別、過去のエンゲージメント率などの特徴をマッピングする。同様に、コンテンツIDは、計算を削減し、オンラインレイテンシを改善するために、パイプラインで事前に計算されるコンテンツの特徴にマッピングされる。

図3:シグナル・エンリッチメント

検索は、いくつかのコンポーネントを呼び出す、分散・収集アプローチである。1つ目は、軽量なスコアリングとターゲティング・フィルターである。スコアリングは、非常にシンプルなモデルを使って、コンテンツの価値を推定する。ターゲティングは、広告主が選択した基準に基づいて、ユーザーの特定のサブセットに広告を制限する。例えば、ユーザーの位置情報に基づいた広告ターゲティングなどである。

図4:検索中の標準クエリ - 分散・収集

次のステップは、予算とペーシングだ。広告の予算が全て終了している場合、その広告は検索されるべきではない。ペーシングは関連する概念であり、広告費を時間全体に分散させる手段である。例えば、広告に100ドルの予算がある場合、広告主は最初の1時間にこの100ドルを使いたくない。広告プラットフォームは、自社のプラットフォーム上のトラフィックの 日次パターンにペースを合わせる傾向がある。

広告の多様性を保証するために、重複排除により広告主が投稿できる広告の数を制限する。プラットフォームは、単一の広告主からの広告だけでフィードを圧倒すべきではない。例えば、広告主ごとに上位K位までの候補だけが次のステージに渡される。最後に、これは分散・収集アプローチであるため、ファネルのさらに下の段階に送る前に、結果をブレンドしなければならない異なる検索ソースがあるかもしれない。

次のステップは候補の選択であり、この分野での最近の進歩である。従来、候補検索はキーワードや広告コピーテキストをマッチングさせるだけのシンプルなものだった。システムが複雑になればなるほど、これを維持するのは難しくなり、反復も難しくなる。

2016年、YouTubeはTwo-Tower Deep Neural Networksを導入することで、こうした検索システムの仕組みを変える画期的な論文を発表した。このアイデアは、ユーザーとコンテンツの特徴に基づいて潜在的な表現を学習するというものだ。これらの表現と特徴は、モデルの中で互いに分離されたまま保たれる。しかし最終的には、ユーザーがコンテンツ・アイテムに関与した場合、これらの表現は互いに非常に近くなるはずであり、それがモデルの学習目的である。

図5:Two-Tower DNN [P Covington et. al, 2016]

このモデルの利点は、広告埋め込みを事前に計算し、キャッシュし、オフラインでインデックスを作成できることだ。広告データベースは、各広告をモデルの広告 "タワー"に通して埋め込みを生成することで、インデックスを構築する。広告が配信時間中にインデックス化されると、検索サーバーはモデルのユーザー部分を呼び出すだけでよくなり、次にHNSWのような近似最近傍検索アルゴリズムを利用して、広告データベースのインデックスから関連する広告を見つけることができる。

図6:Two-Towerモデルの展開

ランキングモデル

次はランキングモデルだ。2014年から始まったモデルは、ロジスティック回帰のような単純化されたものだった。この進化で起こった次のステップは、モデルをより表現豊かにするために、Pinterestは単純化されたソリューションから、GBDT+ロジスティック回帰ソリューションのような、より複雑なモデルに移行した。

モデルが持ちうる特徴には4つのタイプがある。ユーザーの特徴、コンテンツの特徴、履歴におけるユーザーとコンテンツのインタラクション、そして最後に、この印象時間の間に起こったイベントである。モデルはこれらの特徴間のいくつかの非線形インタラクションを学習する必要があり、GBDTはそれが得意である。また、このモデルはロジスティック回帰の枠組みを保持しており、これはカーディナリティの高い特徴を捉える線形モデルである。GBDTはこのような特徴は苦手である。

図7:GBDT+ロジスティック回帰アンサンブルモデル

すぐに、Pinterestは約60のモデルを生産するようになった。これらのモデルは増え続け、製品も成長していた。これらすべてのモデルを維持するのは複雑になり、特徴の採用と削除のサイクルが長くなり、最適なシステムではなくなっていった。

また、この頃、機械学習システムはモデルの提供を容易にサポートしていなかった。Pinterestは、モデルの学習と配信に異なる言語やフレームワークを使っていた。例えば、PinterestはXGBoostを使って学習し、それをTensorFlowモデルに変換し、それをPinterestの提供言語であるC++に変換していた。システム内でこのようなホップが繰り返されると、最適性が損なわれ、新機能の開発サイクルが長くなる。

最後に、新しい広告グループが常に作成されたり削除されたりしていた。広告が生きている期間は、1~2カ月程度かもしれない。Pinterestは、新しいデータ分布に対してより段階的に学習させることができるよう、モデルの応答性を必要としていた。しかし、GBDTモデルは静的であり、段階的に訓練する方法はない。一方、ディープ・ニューラル・ネットワーク(DNN)は、段階的にに学習できる。

次の反復は、GBDTをDNNアプローチに置き換えることだった。DNNは多くの利点をもたらすが、より複雑なモデルだ。ここで変わったことのひとつは、以前の伝統的な機械学習アルゴリズムは、エンジニアが2つの特徴の関連性を定義する、手作業による特徴エンジニアリングに依存していたことだ。モデルは本質的に、それ自体で特徴のインタラクションは学習はできなかった。DNNのアーキテクチャでは、モデルはこれらのインタラクションを学習できる。

業界のほとんどのレコメンデーションモデルは、同様の多層アーキテクチャを共有している。まず、プラットフォームが特徴を定義し、モデルがその特徴をどのように理解するかを定義する表現層がある。この特定のシナリオでは、DNNにとって特徴処理は非常に重要である。異なる特徴間で特徴のスケールが異なると、モデルが壊れる可能性があるため、このレイヤーには、値をつぶしたり、クリッピングしたり、特徴に対して何らかの正規化を行うロジックが含まれる。

図8:PinterestのAutoMLアーキテクチャ

次に、2つの特徴が互いに関連している場合、モデルはそれらを一緒に要約し、共通の埋め込みを学習できる。その後、特徴のインタラクションを学習する乗法クロスレイヤーが続き、完全連結レイヤーが続く。

DNNのもう一つの利点は、異なる目的にまたがるマルチタスク学習である。ネットワークの重みは、クリック数、リピン数、あるいはプラットフォーム上に存在するその他の指標など、さまざまな目的にわたって共有されるため、目的ごとに異なるモデルを訓練する必要がなくなる。

モデルの次の反復は、ユーザーがプラットフォーム上で行っている一連の活動を利用する。例えば、ユーザーがプラットフォーム上で複数のピンや複数のピンや複数の画像とインタラクションしたと仮定しよう。食品関連のピン、家の装飾や旅行関連のピンかもしれない。このアイデアは、プラットフォームが、ユーザーが次に何をするかを定義するために、ユーザーが何をしているかのこの表現を使うことができるかということだ。

これを実装するために、PinterestはTransformer DNNアーキテクチャに注目した。Transformerは、特徴のインタラクションに関する非常に強力な情報をエンコードできる。モデルの重要なパラメータは、最大シーケンス長である。シーケンス長が長くなるにつれて、モデルサイズは二次関数的に大きくなり、これは処理能力に影響を与える。

シーケンス長を例えば100イベントまで増やすと、上述の複雑な特徴は実用的でなくなる。その代わりに、モデルは「その行動はなにか?ユーザーはクリックしたか?」のような単純な特徴を使う。非常に単純な特徴だが、より長いシーケンスによって、モデルはより優れた能力を持つことができる。

Pinterestのオフラインユーザー表現のための最新のモデルアーキテクチャは、PinnerFormerと呼ばれるTransformerエンコーダーに基づいている。このコンポーネントは、昨日から1年前までの過去のユーザーとのエンゲージメントから入力を受け取る。これらのエンゲージメントは全てオフラインでエンコードされ、各ユーザーのエンベッディングを学習し、ダウンストリームDNNモデルへの特徴の入力として使用できる。

図9:PinnerFormer:Pinterestにおけるユーザー表現のためのシーケンスモデリング

このモデルへのもう一つの入力は、現在のユーザー・エンゲージメントから得られるリアルタイムのシーケンスである。この2つを組み合わせることで、ユーザーがプラットフォーム上で何をしているかを学習できる。Pinterestのレコメンデーション・システムを動かしているのは、NLP領域からヒントを得た、これらのシーケンスを活用することだ。

図10:長いシーケンスを組み合わせる

PinterestにおけるMLOps

レコメンデーション・システム全体、そしてそれが本番でどのように展開され、運用されているのかにおいて、機械学習はそのごく一部である。他にも考慮すべきことはたくさんある。例えば、開発チームがより速く反復できるようにするにはどうすればいいか?サービス提供インフラがモデルをサポートできるようにするにはどうすればいいか?リソースをどのように管理するか?どのようにデータを保存し、検証するのか?このような種類のチェックをすべて行うことは非常に重要だ。

以前は、Pinterestの各チームは多くの異なるパイプラインを持っていた。誰もが同じ車輪を再構築していたのだ。Pinterestは、よりスケーラブルな方法でこれを行う必要があった。昨年、ほとんどのイテレーションがそこで行われた。Pinterestは、Dockerイメージと従来のCI/CDサービスを提供する、統一されたPytorchベースのMLフレームワーク(MLEnv)を構築した。ユーザーがコードを書く部分は非常に小さく、様々なMLOpsコンポーネント間の統合はAPIベースのソリューションを通じてシームレスに行われるため、チームはより速く反復できる。

標準的なモデルの 実装プロセスは、オープンソースのソリューションであるMLflowを使用している。これらのモデルは本番パイプラインに移行される際にバージョン管理されるため、チームは簡単にロールバックできる。また、モデルは再現可能である。MLflowにはUIがあり、ユーザーはどのパラメータがトレーニングに使われたかを確認できる。チームが再びトレーニングし、トレーニングプロセスを再評価する必要がある場合、それは簡単に実行できる。

テストとモニタリング

最初のテストステップは、統合テストだ。Pinterestはコード変更が書き込まれると、シャドウトラフィックを通じて本番環境でテストし、変更がデプロイされた場合に何が起こるかを確認できる。メトリクスを自動的にキャプチャすることで、テストプロセス中に見落としがないようにしている。また、特定のリクエストに対して、特定のモデルのバージョンでどのように表示されるかを再生できるデバッグ・システムもある。

次のステップは、コードがシステムにマージされた後のリリース方法だ。Pinterestは、カナリア、ステージング、プロダクションパイプラインの標準的なプロセスに従う。これらの各段階は、ビジネスが気にするリアルタイムのメトリクスを監視する。もし前日比で乖離があったり、本番環境と別の環境の間で乖離があったりした場合、デプロイはシームレスな方法で停止され、ロールバックされる。

最後に、これらすべての安全策にもかかわらず、バグが逃げ出す可能性はある。また、広告主は異なる行動をとるかもしれない。そこでPinterestは、収益、挿入率、QPSといった異なる次元に沿って、前日比、前週比のパターンをシステムに取り込むリアルタイム・モニタリングを行っている。

MLワークフローの検証とモニタリング

生産メトリクスのモニタリングの他に、MLワークフローには追加のモニタリング要件がある。最初のステップは、モデルに投入されるトレーニングデータセットを見ることであり、その上でカバレッジとアラートを定義することである。例えば、特徴とそれらが時間とともにどのように変化するかを監視し、特徴が新鮮であることを保証する。

次に行われるのが、オフラインでのモデル評価だ。訓練されたモデルができたら、開発者はこのモデルが正しく予測するかどうかをチェックする必要がある。Pinterestは、AUCのようなモデルメトリクスを取得するが、予測値を取得し、予測値にスパイクがあるかどうかも確認する。もしそれがあれば、モデルの検証プロセスを停止させることができる。また、本番稼動時の予測スパイクも監視している。

システムをデバッグするために、Pinterestはいくつかの異なるツールを開発した。1つの鍵は、検索、予算管理、インデックス作成、広告主といった広告配信ファネルを可視化することだ。Pinterestのツールは、広告がファネルから削除されている場所を特定するのに役立つ。

例えば、ある広告があまり頻繁に表示されていないとする。配信側で表示されない場合、広告の品質が非常に低いか、オークションで競争力がない可能性がある。別のシナリオとしては、広告主が特定のユーザーにだけ広告を表示したい場合がある。これは非常に厳しい検索シナリオなので、広告が表示されないのはそのためかもしれない。

大きなモデルの配信

もう一つの目標は、配信インフラが低レイテンシーであると確認することである。レイテンシーを改善する1つの方法は、モデルが複雑な場合、GPUサービングに移行することだ。それが難しい場合は、モデルの量子化や知識の蒸留などの最適化技術があり、通常は推論精度を犠牲にしてレイテンシーを改善できる。

結論

Mudgal氏は、Pinterestの広告配信システムの概要と、MLを 本番でどのように大規模に使用しているかを紹介した。彼はまた、Pinterestが本番にデプロイされる前と後のモデルをどのように監視し、テストしているかについても述べた。Mudgal氏は、聴衆が同様の課題を克服するために自社のシステムに適用できるいくつかの洞察を提供した。

作者について

この記事に星をつける

おすすめ度
スタイル

特集コンテンツ一覧

BT