こんにちは、Habr! 少し前に、私はディープラーニングに興味を持ち、ゆっくりとテンソルフローの研究を始めました。 テンソルフローを掘り下げながら、私は並列プログラミングのコースを思い出しました。それは大学の4年目にその年に行いました。 そこでのタスクは次のように策定されました。
2次元熱方程式の線形初期境界値問題:
これを拡散方程式と呼ぶ方が正しいでしょうが。
次に、並列化にMPIを使用し、共役勾配法を使用して、暗黙的なスキームの有限差分法によってタスクを解決する必要がありました。
私はまだ数値法の専門家ではなく、テンソルフローの専門家でもありませんが、すでに経験を積んでいます。 そして、私はディープラーニングのフレームワークに関する教訓を計算しようと熱望していました。 共役勾配法をもう一度実装するのは面白くありませんが、テンソルフローが計算をどのように処理し、どのような困難が生じるかを見るのは面白くなります。 この投稿は、その由来についてです。
数値アルゴリズム
グリッドを定義します。
差分スキーム:ペイントを簡単にするために、演算子を導入します:
明示的な差分スキーム:
明示的な差分スキームの場合、前の瞬間の関数の値が計算に使用され、方程式を解く必要はありません。

。 ただし、このようなスキームは精度が低く、必要なタイムステップが大幅に小さくなります。
暗黙的な差分スキーム:
関連するすべてを左に移動します

、そして右へ

そして乗算する

:
実際、グリッド上で演算子方程式を取得しました。
値を書くとどうなる

通常のベクトルとしてのグリッドノードでは、線形方程式の通常のシステム(

) 前回の値は既に計算されているため、定数です。
便宜上、演算子を提示します

2つの演算子の違いとして:
ここで:
交換

評価に

、エラー関数を書きます:
どこで

-グリッドノードのエラー。
勾配を使用して、汎関数誤差を繰り返し最小化します。
その結果、タスクはテンソルと勾配降下の乗算に削減されました。これがまさにテンソル
フローの目的です。
Tensorflowの実装
テンソルフローについて簡単に
テンソルフローでは、最初に計算グラフが作成されます。 グラフのリソースは
tf.Session内で割り当てられ
ます 。 グラフノードはデータ操作です。 グラフへの入力のセルは
tf.placeholderです。 グラフを実行するには、セッションオブジェクトでrunメソッドを実行し、目的の操作とプレースホルダーの入力データを渡します。
runメソッドは操作の結果を返し、セッション内の
tf.Variable内の値を変更することもできます。
tensorflow自体は、元のグラフに勾配が実装されている操作のみが含まれている場合(まだではない場合)、勾配backpropagationを実装する操作のグラフを作成できます。
コード:
最初に初期化コード。 ここでは、すべての予備操作を実行し、事前に計算できるすべてのものを検討します。

そして

そのように取られるべきです

特に「非平滑」機能を使用する場合は、小さく、できれば少なくとも1未満でした。
方程式のグラフを作成する方法:
良い方法では、与えられたエッジで関数の値を考慮し、内部領域でのみ関数の値を最適化する必要がありましたが、これには問題が生じました。 テンソルの一部のみを最適化できるようにする方法はなく、勾配の割り当ては(投稿を書いている時点で)テンソルスライスの値の割り当て操作に対して書き込まれていません。 エッジをトリッキーに調整したり、独自のオプティマイザーを作成したりできます。 ただし、関数値の境界と境界条件の差をエラー関数に追加するだけでうまく機能します。
誤差汎関数が2次であっても、適応モーメントを使用した方法が最適であることが判明したことは注目に値します。
関数の計算 :各時点で、maxiterを超えるか、エラーがeps未満になるまで、最適化を数回繰り返し、保存して次の瞬間に進みます。
計算 def train_graph(self, eps, maxiter, miniter): g = self.g losses = []
打ち上げ:起動コード tmax = 0.5 nxy = 100 nt = 10000 A = np.array([0.2, 0.5]) B = np.array([0.7, 0.2]) C = np.array([0.5, 0.8]) k1 = 1.0 k2 = 50.0 omega = 20
結果
元の状態:
オリジナルとしての条件、ただしなし

方程式では:
非表示のテキストコードで簡単に修正されます:
デリバティブは関数自体よりも高次であるため、ほとんど違いはありません。


さらにどこでも:
加熱エッジが1つある状態:
最初に加熱された領域を冷却する条件:
野外での加熱を含む条件:
GIF描画
3D GIF関数:
3D GIFメインクラスで、Uをpandas.DataFrameとして返すメソッドを追加します。
def get_U_as_df(self, step=1): nxyp = self._nxy + 1 nxyp2 = nxyp**2 Uf = self._U.reshape((nxy+1)**2,-1)[:, ::step] data = np.hstack((self._vs, Uf)) df = pd.DataFrame(data, columns=["x","y"] + list(range(len(self._ts))[0::step])) return df
def make_gif(Udf, fname): from mpl_toolkits.mplot3d import Axes3D from matplotlib.ticker import LinearLocator, FormatStrFormatter from scipy.interpolate import griddata fig = plt.figure(figsize=(10,7)) ts = list(Udf.columns[2:]) data = Udf
2D GIF描画機能:
2D GIF def make_2d_gif(U, fname, step=1): fig = plt.figure(figsize=(10,7)) zmax = np.max(np.max(U)) + 0.01 zmin = np.min(np.min(U)) - 0.01 norm = matplotlib.colors.Normalize(vmin=zmin, vmax=zmax, clip=False) im=plt.imshow(U[:,:,0], interpolation='bilinear', cmap=cm.coolwarm, norm=norm) plt.grid(False) nst = U.shape[2] // step
まとめ
GPUを使用しない場合の元の状態は4分26秒、GPUを2分11秒使用すると見なされたことは注目に値します。 ポイントの値が大きいと、ギャップが大きくなります。 ただし、結果のグラフのすべての操作がGPU互換ではありません。
機械の特徴:
- Intel Core i7 6700HQ 2600 MHz、
- NVIDIA GeForce GTX 960M。
次のコードを使用して、どの操作が何に対して実行されるかを確認できます。
面白い経験でした。 Tensorflowは、このタスクでうまく機能しました。 たぶん、このアプローチでも何らかの種類のアプリケーションが得られるでしょう-C / C ++よりもPythonでコードを書く方がいいですし、テンソルフローの開発によりさらに簡単になります。
ご清聴ありがとうございました!
中古文学
-Bakhvalov N.S.、Zhidkov N.P.、G.M。Kobelkov
Numerical Methods 、2011